mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Compare commits
356 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f3a6157080 | ||
![]() |
867512eecd | ||
![]() |
f9109d0348 | ||
![]() |
9943406836 | ||
![]() |
18ee9d6c27 | ||
![]() |
31ea1f0928 | ||
![]() |
dad97b36e1 | ||
![]() |
19a1634f4e | ||
![]() |
aa375dc36a | ||
![]() |
a91f8ab4d4 | ||
![]() |
7fbc9ffcc6 | ||
![]() |
579e0e0bcc | ||
![]() |
b26c451eaf | ||
![]() |
ee9e78e80b | ||
![]() |
706b4a7cea | ||
![]() |
f5ba607fa6 | ||
![]() |
572867b7ad | ||
![]() |
47e715ffe5 | ||
![]() |
c445bd1869 | ||
![]() |
28c0267cae | ||
![]() |
4058050423 | ||
![]() |
04d9a36292 | ||
![]() |
29129037b0 | ||
![]() |
c6b7ff82c0 | ||
![]() |
95ee6ff9ae | ||
![]() |
8cc10a1b71 | ||
![]() |
3ef3279527 | ||
![]() |
f48b4235c1 | ||
![]() |
cdf6314bff | ||
![]() |
48411680c6 | ||
![]() |
132029169d | ||
![]() |
483d4300b1 | ||
![]() |
397e8d03a1 | ||
![]() |
d336b75f56 | ||
![]() |
a7af7cf938 | ||
![]() |
2ba937bd32 | ||
![]() |
48d416b5c4 | ||
![]() |
ed2ebb00fd | ||
![]() |
0458267634 | ||
![]() |
a0de6b092b | ||
![]() |
0f2cbe8f04 | ||
![]() |
983105ab6a | ||
![]() |
5aa657e0b5 | ||
![]() |
69ae0385fb | ||
![]() |
a8d74f5885 | ||
![]() |
20b6ea5e85 | ||
![]() |
b8bbb02b20 | ||
![]() |
4896f3061a | ||
![]() |
0aaab6d4b7 | ||
![]() |
a12ea4b112 | ||
![]() |
e023962bfc | ||
![]() |
4df42e10b4 | ||
![]() |
ee4981b6e1 | ||
![]() |
172e260167 | ||
![]() |
fd52c0e5e7 | ||
![]() |
6982476e89 | ||
![]() |
24c1ce73ae | ||
![]() |
d8530a2b8b | ||
![]() |
7a5cd36e6d | ||
![]() |
3bfc350b65 | ||
![]() |
b52bc7bfd4 | ||
![]() |
89abc6a964 | ||
![]() |
45643237e4 | ||
![]() |
e66161176b | ||
![]() |
269c655dc5 | ||
![]() |
15143d1384 | ||
![]() |
0244468624 | ||
![]() |
3ead4d12d4 | ||
![]() |
5277665dd1 | ||
![]() |
79964a0601 | ||
![]() |
e11843e4a0 | ||
![]() |
df52d067e7 | ||
![]() |
1ea663c0b9 | ||
![]() |
73c6821a9a | ||
![]() |
26126bae1a | ||
![]() |
980e450871 | ||
![]() |
c7bb82c1de | ||
![]() |
a287d0e208 | ||
![]() |
50d794d0de | ||
![]() |
332e982bed | ||
![]() |
9ee0dffb33 | ||
![]() |
6094a2c85b | ||
![]() |
b5d14edb94 | ||
![]() |
00a820f007 | ||
![]() |
34466de3a7 | ||
![]() |
bf14553d77 | ||
![]() |
bc2e49fb5d | ||
![]() |
fdc1fb61ba | ||
![]() |
2546b6da21 | ||
![]() |
7178e0376e | ||
![]() |
5510d3aeb5 | ||
![]() |
9554cbf808 | ||
![]() |
082c005a20 | ||
![]() |
5b7491c7a7 | ||
![]() |
018cd4ed52 | ||
![]() |
0aad11d9bc | ||
![]() |
29e6f52c4c | ||
![]() |
25393106b8 | ||
![]() |
fd2e5f2e52 | ||
![]() |
b33f42c9c5 | ||
![]() |
5246cd34c9 | ||
![]() |
a336ac73fb | ||
![]() |
a2a00a488f | ||
![]() |
c7b6d0adee | ||
![]() |
9fe83fb131 | ||
![]() |
62d0d59e74 | ||
![]() |
2e859a1ee6 | ||
![]() |
9b6f5409c2 | ||
![]() |
57c65e2eeb | ||
![]() |
8ba6a04d61 | ||
![]() |
47ceaf9a7b | ||
![]() |
521ab481d7 | ||
![]() |
ac153ec901 | ||
![]() |
fc109cd954 | ||
![]() |
5f73a4c32b | ||
![]() |
f609427e00 | ||
![]() |
edb69472c3 | ||
![]() |
d8d64ee16c | ||
![]() |
db9537a34b | ||
![]() |
577a353285 | ||
![]() |
42353efdff | ||
![]() |
454a4565c0 | ||
![]() |
bf7d10c825 | ||
![]() |
5ae8155866 | ||
![]() |
8354e554e4 | ||
![]() |
eb44ea7f42 | ||
![]() |
96b3293023 | ||
![]() |
69b0d23b45 | ||
![]() |
d481947e34 | ||
![]() |
b5ebcdbcd7 | ||
![]() |
c6ad1f8afe | ||
![]() |
354926a6b3 | ||
![]() |
3767c698bd | ||
![]() |
b115abf081 | ||
![]() |
036f9aa4b9 | ||
![]() |
5555b38fea | ||
![]() |
7bbbf846d8 | ||
![]() |
5b277d633b | ||
![]() |
5bb12915fe | ||
![]() |
93b30241da | ||
![]() |
30a7b9d90e | ||
![]() |
e02cb61efc | ||
![]() |
c01291d15f | ||
![]() |
3c4880e783 | ||
![]() |
3172c32cc7 | ||
![]() |
7d8accb95d | ||
![]() |
fd9d1ec714 | ||
![]() |
7f8f48756d | ||
![]() |
b205b63af3 | ||
![]() |
64a3d5f5aa | ||
![]() |
a40de5ecee | ||
![]() |
99b79eab7e | ||
![]() |
9662054a4f | ||
![]() |
29aecc577b | ||
![]() |
cfdc1a3130 | ||
![]() |
b40387817d | ||
![]() |
7903805373 | ||
![]() |
282b27ff5d | ||
![]() |
1b012fb2d0 | ||
![]() |
54f9cb49f3 | ||
![]() |
b17c9ad106 | ||
![]() |
3764ab4bf9 | ||
![]() |
c1f3187306 | ||
![]() |
a8532b6f78 | ||
![]() |
a9185be603 | ||
![]() |
91bbb25270 | ||
![]() |
2ef8055535 | ||
![]() |
8fe21fddb7 | ||
![]() |
5770df46e4 | ||
![]() |
bd0923cab3 | ||
![]() |
3597a99a08 | ||
![]() |
805d506062 | ||
![]() |
e38b6286c7 | ||
![]() |
805b3e10d2 | ||
![]() |
08b6c81e67 | ||
![]() |
0617bf4b8c | ||
![]() |
21870349d4 | ||
![]() |
eba1b640cf | ||
![]() |
90d3332143 | ||
![]() |
63100d2a37 | ||
![]() |
57009c6f72 | ||
![]() |
778d9b8585 | ||
![]() |
630c67a482 | ||
![]() |
081d96e055 | ||
![]() |
53d40cbe51 | ||
![]() |
da560e1629 | ||
![]() |
bb21b5d8e1 | ||
![]() |
f03324585b | ||
![]() |
bf6e4c8a0f | ||
![]() |
21eda56c9e | ||
![]() |
58af5b658b | ||
![]() |
224c33ec1d | ||
![]() |
3e56549498 | ||
![]() |
77b1844151 | ||
![]() |
a0b0fbd5d6 | ||
![]() |
fb62b2cdb1 | ||
![]() |
1cc4a021c2 | ||
![]() |
aba193ca85 | ||
![]() |
7328f7b99b | ||
![]() |
688ba0388c | ||
![]() |
dabbcd8bcd | ||
![]() |
955688fec0 | ||
![]() |
6f76bdfeb0 | ||
![]() |
4d0777ce52 | ||
![]() |
12cc542a42 | ||
![]() |
5c3aaf0a22 | ||
![]() |
cf68489bf8 | ||
![]() |
024ff58965 | ||
![]() |
21b2b06dab | ||
![]() |
7ee0eb5e88 | ||
![]() |
ee4b14c770 | ||
![]() |
7cc5d368d1 | ||
![]() |
9031d3e5cf | ||
![]() |
6253cd678b | ||
![]() |
7ded4f1662 | ||
![]() |
13df59f806 | ||
![]() |
dc4aa95109 | ||
![]() |
d38a8d6655 | ||
![]() |
36493a89cc | ||
![]() |
6c25a669ec | ||
![]() |
d750201795 | ||
![]() |
62f23bd2db | ||
![]() |
b0330dc5f5 | ||
![]() |
bdcf4e788c | ||
![]() |
a6f406a928 | ||
![]() |
f27015d5bd | ||
![]() |
e5678d2406 | ||
![]() |
d7190d770b | ||
![]() |
21a2c2daf3 | ||
![]() |
90884998a1 | ||
![]() |
7c72526946 | ||
![]() |
0e904f9465 | ||
![]() |
3d6333f4ef | ||
![]() |
f0cdc4919a | ||
![]() |
a7f9d32c3a | ||
![]() |
4574b7317c | ||
![]() |
c3caad4da7 | ||
![]() |
083432da78 | ||
![]() |
fba4534ce4 | ||
![]() |
268910ef63 | ||
![]() |
01aa926f26 | ||
![]() |
d0f74934af | ||
![]() |
825db3567f | ||
![]() |
8af86d9f29 | ||
![]() |
e7cf83e387 | ||
![]() |
c252bf32cc | ||
![]() |
d1d1ab9f5f | ||
![]() |
bcfc745ea6 | ||
![]() |
5d0486c87c | ||
![]() |
d2098e1de5 | ||
![]() |
6c8326fce1 | ||
![]() |
1c9497e316 | ||
![]() |
195755b21e | ||
![]() |
01f03d6726 | ||
![]() |
94eeccb017 | ||
![]() |
127094f9fa | ||
![]() |
5df7071999 | ||
![]() |
aa9a4d429e | ||
![]() |
a1e5bc57d0 | ||
![]() |
e299b867c9 | ||
![]() |
1ec6af8ea0 | ||
![]() |
91273c7763 | ||
![]() |
681b915faa | ||
![]() |
2b42641c6b | ||
![]() |
daabd2ba80 | ||
![]() |
9774e591da | ||
![]() |
5fae1c645d | ||
![]() |
588c78f1d6 | ||
![]() |
4e0ea84fad | ||
![]() |
eb047badfd | ||
![]() |
62486d9bf1 | ||
![]() |
dd21ff6ce1 | ||
![]() |
50f4227266 | ||
![]() |
cd30370c85 | ||
![]() |
94cd2a3309 | ||
![]() |
4a24f3558f | ||
![]() |
baca9f5ca1 | ||
![]() |
7adee39d82 | ||
![]() |
18b24c9ee8 | ||
![]() |
e89d69ba3b | ||
![]() |
837407514d | ||
![]() |
e944951c55 | ||
![]() |
dfa289ce56 | ||
![]() |
0961aef9aa | ||
![]() |
38e900bed7 | ||
![]() |
26eed03397 | ||
![]() |
3087ff9caf | ||
![]() |
68da99c4a0 | ||
![]() |
100e7e6eff | ||
![]() |
817d0ed7e6 | ||
![]() |
36a6dd049b | ||
![]() |
8494463926 | ||
![]() |
0737c7b6b9 | ||
![]() |
39ef78fd4a | ||
![]() |
2cf46fede4 | ||
![]() |
cd7e0b51ea | ||
![]() |
2888402e5d | ||
![]() |
3853236023 | ||
![]() |
d4f0e188fe | ||
![]() |
980d914ffd | ||
![]() |
b8059de996 | ||
![]() |
56bfcbe664 | ||
![]() |
2bad768263 | ||
![]() |
d86a9bd364 | ||
![]() |
93556780eb | ||
![]() |
6645f3c1f3 | ||
![]() |
c988245d5f | ||
![]() |
b349482f20 | ||
![]() |
37f9d3e8f8 | ||
![]() |
0f647978fa | ||
![]() |
d20712f301 | ||
![]() |
d715d169fe | ||
![]() |
de393d4ec0 | ||
![]() |
43fdb4a2f3 | ||
![]() |
297c56b289 | ||
![]() |
b2553fcabe | ||
![]() |
10a60abd1a | ||
![]() |
291f858488 | ||
![]() |
a718d99672 | ||
![]() |
efd6ca1349 | ||
![]() |
2f1ff9950c | ||
![]() |
9816417b21 | ||
![]() |
7a2e2519cd | ||
![]() |
ce1f8955e9 | ||
![]() |
2a8f8713e7 | ||
![]() |
0e871e477a | ||
![]() |
0d8bc06f04 | ||
![]() |
689a0fb55c | ||
![]() |
bd72fe9153 | ||
![]() |
1ff3ff43e4 | ||
![]() |
552c304be4 | ||
![]() |
df70353608 | ||
![]() |
f8138b04eb | ||
![]() |
24abab7746 | ||
![]() |
2f6b63afe8 | ||
![]() |
5888457926 | ||
![]() |
1645c5b337 | ||
![]() |
8444855f24 | ||
![]() |
be96cd98ef | ||
![]() |
ba39ba49d9 | ||
![]() |
98ba854ae8 | ||
![]() |
0fb9078a1e | ||
![]() |
9462d1e30b | ||
![]() |
2536639272 | ||
![]() |
38c88e78f3 | ||
![]() |
ec9ac18fa4 | ||
![]() |
ce7547f71e | ||
![]() |
77706ce200 | ||
![]() |
6136c92996 | ||
![]() |
f4418672ba | ||
![]() |
9a898b72e2 | ||
![]() |
dd1ee5139d | ||
![]() |
555cc16a11 | ||
![]() |
fd0b72af1e | ||
![]() |
a7897f30e6 | ||
![]() |
ca33040cfe |
243 changed files with 8763 additions and 6035 deletions
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
|
@ -16,13 +16,11 @@
|
|||
|
||||
### Settings
|
||||
|
||||
<!-- Please fill in the settings used below from USER_SETTINGS.h, as it will help with diagnosis. -->
|
||||
<!-- Please attach screenshot of configuration for easier debugging, as it will help with diagnosis. -->
|
||||
|
||||
- Software version: ``
|
||||
- Battery used: ``
|
||||
- Inverter communication protocol: ``
|
||||
- Hardware used for Battery-Emulator: `HW_LILYGO, HW_STARK, Custom`
|
||||
- CONTACTOR_CONTROL: `yes/no`
|
||||
- CAN_ADDON: `yes/no`
|
||||
- WEBSERVER: `yes/no`
|
||||
- MQTT: `yes/no`
|
||||
- Software version: X.Y.Z
|
||||
- Battery used: [Nissan LEAF]
|
||||
- Inverter communication protocol: [BYD Modbus]
|
||||
- Hardware used for Battery-Emulator: [LilyGo T-CAN/Stark CMR/LilyGo T-2CAN]
|
||||
- GPIO controlled contactors: [yes/no]
|
||||
- MQTT: [yes/no]
|
||||
|
|
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -20,10 +20,10 @@ A clear and concise description of what you expected to happen.
|
|||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version and settings (please complete the following information):**
|
||||
- Please attach your USER_SETTINGS files for easier debugging
|
||||
- Please attach screenshot of configuration for easier debugging
|
||||
- Software version: X.Y.Z
|
||||
- Battery used: [NISSAN_LEAF]
|
||||
- Inverter communication protocol: [BYD_MODBUS]
|
||||
- Hardware used for Battery-Emulator: [HW_LILYGO/HW_STARK/HW_DEVKIT]
|
||||
- CONTACTOR_CONTROL: [yes/no]
|
||||
- Battery used: [Nissan LEAF]
|
||||
- Inverter communication protocol: [BYD Modbus]
|
||||
- Hardware used for Battery-Emulator: [LilyGo T-CAN/Stark CMR/LilyGo T-2CAN]
|
||||
- GPIO controlled contactors: [yes/no]
|
||||
- MQTT: [yes/no]
|
||||
|
|
93
.github/workflows/compile-all-hardware.yml
vendored
93
.github/workflows/compile-all-hardware.yml
vendored
|
@ -1,93 +0,0 @@
|
|||
# This is the name of the workflow, visible on GitHub UI.
|
||||
name: 🤖 Compile All Hardware
|
||||
|
||||
# Here we tell GitHub when to run the workflow.
|
||||
on:
|
||||
# The workflow is run when a commit is pushed or for a
|
||||
# Pull Request.
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
# This is the list of jobs that will be run concurrently.
|
||||
jobs:
|
||||
# This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request
|
||||
skip-duplicate-actions:
|
||||
runs-on: ubuntu-latest
|
||||
# Map a step output to a job output
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||
concurrent_skipping: 'never'
|
||||
skip_after_successful_duplicate: 'true'
|
||||
do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'
|
||||
|
||||
# Since we use a build matrix, the actual number of jobs
|
||||
# started depends on how many configurations the matrix
|
||||
# will produce.
|
||||
|
||||
# This is the name of the job.
|
||||
build-hardware:
|
||||
needs: skip-duplicate-actions
|
||||
if: needs.skip-duplicate-actions.outputs.should_skip != 'true'
|
||||
|
||||
# Here we tell GitHub that the jobs must be determined
|
||||
# dynamically depending on a matrix configuration.
|
||||
strategy:
|
||||
# The matrix will produce one job for each combination of parameters.
|
||||
matrix:
|
||||
# This is the development board hardware for which the code will be compiled.
|
||||
# FBQN stands for "fully qualified board name", and is used by Arduino to define the hardware to compile for.
|
||||
fqbn:
|
||||
- esp32:esp32:esp32
|
||||
# further ESP32 chips
|
||||
#- esp32:esp32:esp32c3
|
||||
#- esp32:esp32:esp32c2
|
||||
#- esp32:esp32:esp32c6
|
||||
#- esp32:esp32:esp32h2
|
||||
#- esp32:esp32:esp32s3
|
||||
# These are the batteries for which the code will be compiled.
|
||||
battery:
|
||||
- NISSAN_LEAF_BATTERY
|
||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||
inverter:
|
||||
- BYD_CAN
|
||||
# These are the supported hardware platforms for which the code will be compiled.
|
||||
hardware:
|
||||
- HW_LILYGO
|
||||
- HW_STARK
|
||||
- HW_3LB
|
||||
- HW_DEVKIT
|
||||
|
||||
# This is the platform GitHub will use to run our workflow.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# This is the list of steps this job will run.
|
||||
steps:
|
||||
# First we clone the repo using the `checkout` action.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
- name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h
|
||||
|
||||
# We use the `arduino/setup-arduino-cli` action to install and
|
||||
# configure the Arduino CLI on the system.
|
||||
- name: Setup Arduino CLI
|
||||
uses: arduino/setup-arduino-cli@v2
|
||||
|
||||
# We then install the platform.
|
||||
- name: Install platform
|
||||
run: |
|
||||
arduino-cli core update-index
|
||||
arduino-cli core install esp32:esp32
|
||||
|
||||
# Finally, we compile the sketch, using the FQBN that was set
|
||||
# in the build matrix, and using build flags to define the
|
||||
# battery and inverter set in the build matrix.
|
||||
- name: Compile Sketch
|
||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
54
.github/workflows/compile-common-image-lilygo-2CAN.yml
vendored
Normal file
54
.github/workflows/compile-common-image-lilygo-2CAN.yml
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
name: 🔋 Compile Common Image for Lilygo 2CAN
|
||||
|
||||
on:
|
||||
# The workflow is run when a commit is pushed or for a
|
||||
# Pull Request.
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
# This is the list of jobs that will be run concurrently.
|
||||
jobs:
|
||||
# This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request
|
||||
skip-duplicate-actions:
|
||||
runs-on: ubuntu-latest
|
||||
# Map a step output to a job output
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||
concurrent_skipping: 'never'
|
||||
skip_after_successful_duplicate: 'true'
|
||||
do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'
|
||||
|
||||
build-common-image:
|
||||
# This is the platform GitHub will use to run our workflow.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
name: Checkout code
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install PlatformIO Core
|
||||
run: pip install --upgrade platformio
|
||||
|
||||
- name: Build image for Lilygo
|
||||
run: pio run -e lilygo_2CAN_330
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: battery-emulator-lilygo.bin
|
||||
path: .pio/build/lilygo_2CAN_330/firmware.bin
|
|
@ -1,4 +1,4 @@
|
|||
name: 🔋 Compile Common Image for Lilygo
|
||||
name: 🔋 Compile Common Image for Lilygo T-CAN
|
||||
|
||||
on:
|
||||
# The workflow is run when a commit is pushed or for a
|
||||
|
@ -28,7 +28,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
name: Checkout code
|
||||
|
||||
- uses: actions/cache@v4
|
||||
|
@ -38,16 +38,12 @@ jobs:
|
|||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install PlatformIO Core
|
||||
run: pip install --upgrade platformio
|
||||
|
||||
# Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
- name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h
|
||||
|
||||
- name: Build image for Lilygo
|
||||
run: pio run -e lilygo_330
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
name: Checkout code
|
||||
|
||||
- uses: actions/cache@v4
|
||||
|
@ -38,16 +38,12 @@ jobs:
|
|||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install PlatformIO Core
|
||||
run: pip install --upgrade platformio
|
||||
|
||||
# Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
- name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h
|
||||
|
||||
- name: Build image for Stark CMR
|
||||
run: pio run -e stark_330
|
||||
|
||||
|
|
22
.github/workflows/release-assets.yml
vendored
22
.github/workflows/release-assets.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Checkout code at tag
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: refs/tags/${{ steps.vars.outputs.tag }}
|
||||
|
||||
|
@ -41,26 +41,32 @@ jobs:
|
|||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install PlatformIO Core
|
||||
run: pip install --upgrade platformio
|
||||
|
||||
# Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
- name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
|
||||
run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h
|
||||
|
||||
- name: 🛠 Build ota image for Lilygo
|
||||
- name: 🛠 Build ota image for Lilygo T-CAN
|
||||
run: |
|
||||
pio run -e lilygo_330
|
||||
cp .pio/build/lilygo_330/firmware.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-CAN485.ota.bin
|
||||
|
||||
- name: 🛠 Build factory image for Lilygo
|
||||
- name: 🛠 Build factory image for Lilygo T-CAN
|
||||
run: |
|
||||
esptool --chip esp32 merge-bin -o .pio/build/lilygo_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_330/bootloader.bin 0x8000 .pio/build/lilygo_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_330/firmware.bin
|
||||
mv .pio/build/lilygo_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-CAN485.factory.bin
|
||||
|
||||
- name: 🛠 Build ota image for Lilygo 2-CAN
|
||||
run: |
|
||||
pio run -e lilygo_2CAN_330
|
||||
cp .pio/build/lilygo_2CAN_330/firmware.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.ota.bin
|
||||
|
||||
- name: 🛠 Build factory image for Lilygo 2-CAN
|
||||
run: |
|
||||
esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 16MB 0x0000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin
|
||||
mv .pio/build/lilygo_2CAN_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.factory.bin
|
||||
|
||||
- name: 🛠 Build ota image for Stark
|
||||
run: |
|
||||
pio run -e stark_330
|
||||
|
|
2
.github/workflows/run-pre-commit.yml
vendored
2
.github/workflows/run-pre-commit.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Run pre-commit
|
||||
uses: pre-commit/action@v3.0.1
|
||||
|
|
3
.github/workflows/unit-tests.yml
vendored
3
.github/workflows/unit-tests.yml
vendored
|
@ -9,11 +9,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Configure and build with CMake
|
||||
run: |
|
||||
cp Software/USER_SECRETS.TEMPLATE.h Software/USER_SECRETS.h
|
||||
cd test
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -4,6 +4,15 @@
|
|||
# Ignore any files in any build folder
|
||||
*build/
|
||||
|
||||
# Ignore any generated test files from running locally
|
||||
test/CMakeCache.txt
|
||||
test/CMakeFiles/
|
||||
test/CTestTestfile.cmake
|
||||
test/Makefile
|
||||
test/cmake_install.cmake
|
||||
test/gtest/
|
||||
test/tests[1]_include.cmake
|
||||
|
||||
# Ignore .exe (unit tests)
|
||||
*.exe
|
||||
**/.DS_Store
|
||||
|
|
|
@ -10,7 +10,7 @@ ci:
|
|||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v20.1.8
|
||||
rev: v21.1.0
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [-Werror] # change formatting warnings to errors, hook includes -i (Inplace edit) by default
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
### Contributing to the Battery-Emulator project
|
||||
|
||||
What can I do?
|
||||
What can I do? 🦸
|
||||
--------------
|
||||
|
||||
**"Help - I want to contribute something, but I don't know what?"**
|
||||
|
@ -15,10 +15,66 @@ You're in luck. There's various sources to contribute:
|
|||
- [Discussion page](https://github.com/dalathegreat/Battery-Emulator/discussions)
|
||||
- [Discord server](https://www.patreon.com/dala)
|
||||
|
||||
## Notes on embedded system
|
||||
The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the FUNCTION_TIME_MEASUREMENT option in the USER_SETTINGS.h file
|
||||
## Notes on embedded system 🕙
|
||||
The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the "Enable performance profiling:" option in the webserver
|
||||
|
||||
## Code formatting
|
||||
## Setting up the compilation environment (VScode + PlatformIO) 💻
|
||||
|
||||
This project uses the PlatformIO extension within Visual Studio Code for development and uploading. It handles all the complex toolchains and library management for you.
|
||||
### 1. Installing VSCode
|
||||
- Download the stable build of Visual Studio Code for your operating system (Windows, macOS, or Linux) from the official website: https://code.visualstudio.com/
|
||||
- Run the installer and follow the setup instructions.
|
||||
- (Recommended) Launch VSCode after installation.
|
||||
|
||||
### 2. Installing the PlatformIO IDE Plugin
|
||||
|
||||
PlatformIO is an extension that adds all the necessary functionality to VSCode.
|
||||
- Inside VSCode, open the Extensions view by:
|
||||
- Clicking the Extensions icon in the Activity Bar on the left side.
|
||||
- Or using the keyboard shortcut: Ctrl+Shift+X (Windows/Linux) or Cmd+Shift+X (macOS).
|
||||
- In the extensions search bar, type: PlatformIO IDE.
|
||||
- Find the extension published by PlatformIO and click the Install button.
|
||||
- Wait for the installation to complete. This may take a few minutes as PlatformIO downloads and installs its core tools in the background. VSCode might require a reload once finished.
|
||||
|
||||
### 3. Opening the Project and Building
|
||||
- Clone the repository to your local machine using Git.
|
||||
- In VSCode:
|
||||
- Go to File > Open Folder...
|
||||
- Navigate to and select the root folder of the cloned project (the folder containing the platformio.ini file).
|
||||
- Click Open.
|
||||
- PlatformIO will automatically recognize the project structure and begin indexing the code. You'll see the PlatformIO icon (a grey alien) appear in the Activity Bar on the left.
|
||||
|
||||
- To verify everything is set up correctly, build/compile the project:
|
||||
- Click the PlatformIO icon in the Activity Bar to open the PIO Home screen.
|
||||
- Go to Quick Access > PIO > Build.
|
||||
- Alternatively, you can use the checkmark icon in the blue status bar at the bottom of the VSCode window, or the keyboard shortcut Ctrl+Alt+B (Windows/Linux) / Cmd+Alt+B (macOS).
|
||||
- The build process will start. You can monitor the output in the integrated terminal. A successful build will end with ===== [SUCCESS] Took X.XX seconds =====.
|
||||
|
||||
### 4. Uploading Code to Board via USB
|
||||
|
||||
- Connect your Battery-Emulator hardware to your computer using a USB cable.
|
||||
- Select the right board type (Stark, LilyGo)
|
||||
- At the bottom left of VScode, click the Env to bring up a menu of boards. Select the board you are using
|
||||
<img width="396" height="95" alt="image" src="https://github.com/user-attachments/assets/23b73442-5016-4ff1-be78-13e3c41772d5" />
|
||||
<img width="679" height="177" alt="image" src="https://github.com/user-attachments/assets/2fad40a6-f388-4cd6-8e08-844602bb0442" />
|
||||
|
||||
- Ensure the correct upload port is set in the platformio.ini file (it's often auto-detected, but you may need to set it manually. See Troubleshooting below).
|
||||
- Upload the code:
|
||||
- Click the PlatformIO icon in the Activity Bar.
|
||||
- Go to Quick Access > PIO > Upload.
|
||||
- Alternatively, use the right-arrow icon (→) in the blue status bar at the bottom of the VSCode window, or the keyboard shortcut Ctrl+Alt+U (Windows/Linux) / Cmd+Alt+U (macOS).
|
||||
- The upload process will begin. The board may reset automatically. A successful upload will end with ===== [SUCCESS] Took X.XX seconds =====.
|
||||
|
||||
### ⚠️ Troubleshooting & Tips
|
||||
|
||||
#### "Upload port not found" or wrong port errors:
|
||||
|
||||
- Find the correct port:
|
||||
- Windows: Check Device Manager under "Ports (COM & LPT)". It's usually COM3, COM4, etc.
|
||||
- macOS/Linux: Run ls /dev/tty.* or ls /dev/ttyUSB* in a terminal. It's often /dev/tty.usbserial-XXX or /dev/ttyUSB0.
|
||||
- Add the line upload_port = COM4 (replace COM4 with your port) to your platformio.ini file in the [env:...] section.
|
||||
|
||||
## Code formatting 📜
|
||||
The project enforces a specific code formatting in the workflows. To get your code formatted properly, it is easiest to use a pre-commit hook before pushing the code to a pull request.
|
||||
|
||||
Before you begin, make sure you have installed Python on the system!
|
||||
|
@ -38,3 +94,15 @@ Or force it to check all files with
|
|||
```
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
## Local Unit test run 🧪
|
||||
The Unit tests run gtest. Here is how to install this on Debian/Ubuntu and run it locally
|
||||
```
|
||||
sudo apt-get install libgtest-dev
|
||||
sudo apt-get install cmake
|
||||
```
|
||||
Navigate to Battery-Emulator/test folder
|
||||
```
|
||||
sudo cmake CMakeLists.txt
|
||||
sudo make
|
||||
```
|
||||
|
|
56
README.md
56
README.md
|
@ -2,7 +2,7 @@
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## What is Battery Emulator?
|
||||
|
@ -33,52 +33,18 @@ At the same time, EV manufacturers have been putting high capacity battery packs
|
|||
|
||||
For examples showing wiring, see each battery type's own Wiki page. For instance the [Nissan LEAF page](https://github.com/dalathegreat/Battery-Emulator/wiki/Battery:-Nissan-LEAF---e%E2%80%90NV200)
|
||||
|
||||
## How to compile the software 💻
|
||||
## How to install the software 💻
|
||||
|
||||
Start by watching this [quickstart guide](https://www.youtube.com/watch?v=hcl2GdHc0Y0)
|
||||
Start by watching this [quickstart guide](https://www.youtube.com/watch?v=sR3t7j0R9Z0)
|
||||
|
||||
[](https://www.youtube.com/watch?v=hcl2GdHc0Y0)
|
||||
[](https://www.youtube.com/watch?v=sR3t7j0R9Z0)
|
||||
|
||||
1. Download the Arduino IDE: https://www.arduino.cc/en/software
|
||||
2. Open the Arduino IDE.
|
||||
3. Click `File` menu -> `Preferences` -> `Additional Development` -> `Additional Board Manager URLs` -> Enter the URL in the input box: `https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json` and click OK.
|
||||
4. Click `Tools` menu -> `Board: "...."` -> `Boards Manager...`, install the `esp32` package by `Espressif Systems` (not `Arduino ESP32 Boards`), then press `Close`.
|
||||
|
||||
**NOTE: The ESP32 version depends on which release of Battery-Emulator you are running! See the [Release Notes](https://github.com/dalathegreat/Battery-Emulator/releases) for more info on which version to use with the current version (Suggested ESP32 version X.Y.Z)**
|
||||
|
||||

|
||||
|
||||
5. The Arduino board should be set to `ESP32 Dev Module` and `Partition Scheme` to `Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)` (under `Tools` -> `Board` -> `ESP32 Arduino`) with the following settings:
|
||||
|
||||

|
||||
|
||||
6. Select which battery type you will use, along with other optional settings. This is done in the `USER_SETTINGS.h` file.
|
||||
7. Copy the `USER_SECRETS.TEMPLATE.h` file to `USER_SECRETS.h` and update connectivity settings inside this file.
|
||||
8. Press `Verify` and `Upload` to send the sketch to the board.
|
||||
NOTE: In some cases, the LilyGo must be powered through the main power connector instead of USB-C
|
||||
when performing the initial firmware upload.
|
||||
NOTE: On Mac, the following USB driver may need to be installed: https://github.com/WCHSoftGroup/ch34xser_macos
|
||||
|
||||
NOTE: If you see garbled messages on the serial console, change the serial console to match the baud rate to the code, currently 115200.
|
||||
|
||||
NOTE: If you see the error `Sketch too big` then check you set the Partition Scheme above correctly.
|
||||
|
||||
This video explains all the above mentioned steps:
|
||||
<https://youtu.be/_mH2AjnAjDk>
|
||||
|
||||
|
||||
### Linux Development Environment Setup
|
||||
In addition to the steps above, ESP32 requires a dependency for a Python module, pyserial install using the cli.\
|
||||
```python3 -m pip install pyserial```
|
||||
|
||||
If you're using Ubuntu , use apt to manage the dependencies of arduino:\
|
||||
pyserial install: ```sudo apt install python3-serial```
|
||||
|
||||
Arduino AppImage must be set as executable after downloading to run correctly\
|
||||
example: ```chmod 775 arduino-ide_2.3.3_Linux_64bit.AppImage```
|
||||
|
||||
Also you might need to install FUSE to run appimages
|
||||
```sudo apt install libfuse2```
|
||||
1. Open the [webinstaller page](https://dalathegreat.github.io/BE-Web-Installer/)
|
||||
2. Follow the instructions on that page to install the software
|
||||
3. After successful installation, connect to the wireless network (Battery-Emulator , password: 123456789)
|
||||
4. Go to setup page and configure component selection
|
||||
5. (OPTIONAL, connect the board to your home Wifi)
|
||||
6. Connect your battery and inverter to the board and you are done! 🔋⚡
|
||||
|
||||
## Dependencies 📖
|
||||
This code uses the following excellent libraries:
|
||||
|
@ -88,7 +54,7 @@ This code uses the following excellent libraries:
|
|||
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
||||
- [ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
|
||||
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
||||
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
||||
- [pierremolinaro/acan-esp32](https://github.com/pierremolinaro/acan-esp32) MIT-License
|
||||
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
||||
- [pierremolinaro/acan2517FD](https://github.com/pierremolinaro/acan2517FD) MIT-License
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
/* Do not change any code below this line unless you are sure what you are doing */
|
||||
/* Only change battery specific settings in "USER_SETTINGS.h" */
|
||||
#include <Arduino.h>
|
||||
#include "HardwareSerial.h"
|
||||
#include "USER_SECRETS.h"
|
||||
#include "USER_SETTINGS.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_timer.h"
|
||||
|
@ -26,31 +23,28 @@
|
|||
#include "src/devboard/utils/logging.h"
|
||||
#include "src/devboard/utils/time_meas.h"
|
||||
#include "src/devboard/utils/timer.h"
|
||||
#include "src/devboard/utils/types.h"
|
||||
#include "src/devboard/utils/value_mapping.h"
|
||||
#include "src/devboard/webserver/webserver.h"
|
||||
#include "src/devboard/wifi/wifi.h"
|
||||
#include "src/inverter/INVERTERS.h"
|
||||
|
||||
#if !defined(HW_LILYGO) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT)
|
||||
#error You must select a target hardware in the USER_SETTINGS.h file!
|
||||
#if !defined(HW_LILYGO) && !defined(HW_LILYGO2CAN) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT)
|
||||
#error You must select a target hardware!
|
||||
#endif
|
||||
|
||||
#ifdef PERIODIC_BMS_RESET_AT
|
||||
#include "src/devboard/utils/ntp_time.h"
|
||||
#endif
|
||||
volatile unsigned long long bmsResetTimeOffset = 0;
|
||||
|
||||
// The current software version, shown on webserver
|
||||
const char* version_number = "9.0.RC2";
|
||||
const char* version_number = "9.2.dev";
|
||||
|
||||
// Interval timers
|
||||
volatile unsigned long currentMillis = 0;
|
||||
unsigned long previousMillis10ms = 0;
|
||||
unsigned long previousMillisUpdateVal = 0;
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
// Task time measurement for debugging
|
||||
MyTimer core_task_timer_10s(INTERVAL_10_S);
|
||||
#endif
|
||||
uint64_t start_time_10ms = 0;
|
||||
uint64_t start_time_values = 0;
|
||||
uint64_t start_time_cantx = 0;
|
||||
TaskHandle_t main_loop_task;
|
||||
TaskHandle_t connectivity_loop_task;
|
||||
TaskHandle_t logging_loop_task;
|
||||
|
@ -58,140 +52,41 @@ TaskHandle_t mqtt_loop_task;
|
|||
|
||||
Logging logging;
|
||||
|
||||
// Initialization
|
||||
void setup() {
|
||||
init_hal();
|
||||
std::string mqtt_user; //TODO, move?
|
||||
std::string mqtt_password; //TODO, move?
|
||||
std::string http_username; //TODO, move?
|
||||
std::string http_password; //TODO, move?
|
||||
|
||||
init_serial();
|
||||
static std::list<Transmitter*> transmitters;
|
||||
void register_transmitter(Transmitter* transmitter) {
|
||||
transmitters.push_back(transmitter);
|
||||
DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size());
|
||||
}
|
||||
|
||||
// We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set.
|
||||
logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number);
|
||||
|
||||
init_events();
|
||||
|
||||
init_stored_settings();
|
||||
|
||||
if (wifi_enabled) {
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&connectivity_loop, "connectivity_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
|
||||
&connectivity_loop_task, esp32hal->WIFICORE());
|
||||
// Initialization functions
|
||||
void init_serial() {
|
||||
// Init Serial monitor
|
||||
Serial.begin(115200);
|
||||
#if HW_LILYGO2CAN
|
||||
// Wait up to 100ms for Serial to be available. On the ESP32S3 Serial is
|
||||
// provided by the USB controller, so will only work if the board is connected
|
||||
// to a computer.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (Serial)
|
||||
break;
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (!led_init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD)
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
|
||||
&logging_loop_task, esp32hal->WIFICORE());
|
||||
#endif
|
||||
|
||||
if (!init_contactors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!init_precharge_control()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setup_charger();
|
||||
|
||||
if (!setup_inverter()) {
|
||||
return;
|
||||
}
|
||||
setup_battery();
|
||||
setup_can_shunt();
|
||||
|
||||
// Init CAN only after any CAN receivers have had a chance to register.
|
||||
if (!init_CAN()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!init_rs485()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!init_equipment_stop_button()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// BOOT button at runtime is used as an input for various things
|
||||
pinMode(0, INPUT_PULLUP);
|
||||
|
||||
check_reset_reason();
|
||||
|
||||
// Initialize Task Watchdog for subscribed tasks
|
||||
esp_task_wdt_config_t wdt_config = {// 5s should be enough for the connectivity tasks (which are all contending
|
||||
// for the same core) to yield to each other and reset their watchdogs.
|
||||
.timeout_ms = INTERVAL_5_S,
|
||||
// We don't benefit from idle task watchdogs, our critical loops have their
|
||||
// own. The idle watchdogs can cause nuisance reboots under heavy load.
|
||||
.idle_core_mask = 0,
|
||||
// Panic (and reboot) on timeout
|
||||
.trigger_panic = true};
|
||||
#ifdef CONFIG_ESP_TASK_WDT
|
||||
// ESP-IDF will have already initialized it, so reconfigure.
|
||||
// Arduino and PlatformIO have different watchdog defaults, so we reconfigure
|
||||
// for consistency.
|
||||
esp_task_wdt_reconfigure(&wdt_config);
|
||||
#else
|
||||
// Otherwise initialize it for the first time.
|
||||
esp_task_wdt_init(&wdt_config);
|
||||
while (!Serial) {}
|
||||
#endif
|
||||
|
||||
// Start tasks
|
||||
|
||||
if (mqtt_enabled) {
|
||||
if (!init_mqtt()) {
|
||||
return;
|
||||
}
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
|
||||
esp32hal->WIFICORE());
|
||||
}
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,
|
||||
esp32hal->CORE_FUNCTION_CORE());
|
||||
|
||||
#ifdef PERIODIC_BMS_RESET_AT
|
||||
bmsResetTimeOffset = getTimeOffsetfromNowUntil(PERIODIC_BMS_RESET_AT);
|
||||
if (bmsResetTimeOffset == 0) {
|
||||
set_event(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED, 0);
|
||||
} else {
|
||||
set_event(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF("setup() complete\n");
|
||||
}
|
||||
|
||||
// Loop empty, all functionality runs in tasks
|
||||
void loop() {}
|
||||
|
||||
#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD)
|
||||
void logging_loop(void*) {
|
||||
|
||||
init_logging_buffers();
|
||||
init_sdcard();
|
||||
|
||||
while (true) {
|
||||
#ifdef LOG_TO_SD
|
||||
write_log_to_sdcard();
|
||||
#endif
|
||||
#ifdef LOG_CAN_TO_SD
|
||||
write_can_frame_to_sdcard();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void connectivity_loop(void*) {
|
||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
// Init wifi
|
||||
init_WiFi();
|
||||
|
||||
if (webserver_enabled) {
|
||||
init_webserver();
|
||||
}
|
||||
init_webserver();
|
||||
|
||||
if (mdns_enabled) {
|
||||
init_mDNS();
|
||||
|
@ -201,9 +96,7 @@ void connectivity_loop(void*) {
|
|||
START_TIME_MEASUREMENT(wifi);
|
||||
wifi_monitor();
|
||||
|
||||
if (webserver_enabled) {
|
||||
ota_monitor();
|
||||
}
|
||||
ota_monitor();
|
||||
|
||||
END_TIME_MEASUREMENT_MAX(wifi, datalayer.system.status.wifi_task_10s_max_us);
|
||||
|
||||
|
@ -212,151 +105,22 @@ void connectivity_loop(void*) {
|
|||
}
|
||||
}
|
||||
|
||||
void mqtt_loop(void*) {
|
||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
void logging_loop(void*) {
|
||||
|
||||
init_logging_buffers();
|
||||
init_sdcard();
|
||||
|
||||
while (true) {
|
||||
START_TIME_MEASUREMENT(mqtt);
|
||||
mqtt_loop();
|
||||
END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us);
|
||||
esp_task_wdt_reset(); // Reset watchdog
|
||||
delay(1);
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
write_log_to_sdcard();
|
||||
}
|
||||
|
||||
if (datalayer.system.info.CAN_SD_logging_active) {
|
||||
write_can_frame_to_sdcard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::list<Transmitter*> transmitters;
|
||||
|
||||
void register_transmitter(Transmitter* transmitter) {
|
||||
transmitters.push_back(transmitter);
|
||||
DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size());
|
||||
}
|
||||
|
||||
void core_loop(void*) {
|
||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks
|
||||
|
||||
while (true) {
|
||||
|
||||
START_TIME_MEASUREMENT(all);
|
||||
START_TIME_MEASUREMENT(comm);
|
||||
|
||||
monitor_equipment_stop_button();
|
||||
|
||||
// Input, Runs as fast as possible
|
||||
receive_can(); // Receive CAN messages
|
||||
receive_rs485(); // Process serial2 RS485 interface
|
||||
|
||||
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
||||
|
||||
if (webserver_enabled) {
|
||||
START_TIME_MEASUREMENT(ota);
|
||||
ElegantOTA.loop();
|
||||
END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us);
|
||||
}
|
||||
|
||||
// Process
|
||||
currentMillis = millis();
|
||||
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
|
||||
if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) &&
|
||||
(milliseconds(currentMillis) > esp32hal->BOOTUP_TIME())) {
|
||||
set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms));
|
||||
}
|
||||
previousMillis10ms = currentMillis;
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
START_TIME_MEASUREMENT(time_10ms);
|
||||
#endif
|
||||
led_exe();
|
||||
handle_contactors(); // Take care of startup precharge/contactor closing
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
handle_precharge_control(currentMillis);
|
||||
#endif // PRECHARGE_CONTROL
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (currentMillis - previousMillisUpdateVal >= INTERVAL_1_S) {
|
||||
previousMillisUpdateVal = currentMillis; // Order matters on the update_loop!
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
START_TIME_MEASUREMENT(time_values);
|
||||
#endif
|
||||
update_pause_state(); // Check if we are OK to send CAN or need to pause
|
||||
|
||||
// Fetch battery values
|
||||
if (battery) {
|
||||
battery->update_values();
|
||||
}
|
||||
|
||||
if (battery2) {
|
||||
battery2->update_values();
|
||||
check_interconnect_available();
|
||||
}
|
||||
update_calculated_values();
|
||||
update_machineryprotection(); // Check safeties
|
||||
|
||||
// Update values heading towards inverter
|
||||
if (inverter) {
|
||||
inverter->update_values();
|
||||
}
|
||||
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
||||
#endif
|
||||
}
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
START_TIME_MEASUREMENT(cantx);
|
||||
#endif
|
||||
|
||||
// Let all transmitter objects send their messages
|
||||
for (auto& transmitter : transmitters) {
|
||||
transmitter->transmit(currentMillis);
|
||||
}
|
||||
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
||||
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
||||
#endif
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) {
|
||||
// Update worst case total time
|
||||
datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us;
|
||||
// Record snapshots of task times
|
||||
datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us;
|
||||
datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us;
|
||||
datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us;
|
||||
datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us;
|
||||
datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us;
|
||||
}
|
||||
|
||||
datalayer.system.status.core_task_max_us =
|
||||
MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us);
|
||||
if (core_task_timer_10s.elapsed()) {
|
||||
datalayer.system.status.time_ota_us = 0;
|
||||
datalayer.system.status.time_comm_us = 0;
|
||||
datalayer.system.status.time_10ms_us = 0;
|
||||
datalayer.system.status.time_values_us = 0;
|
||||
datalayer.system.status.time_cantx_us = 0;
|
||||
datalayer.system.status.core_task_10s_max_us = 0;
|
||||
datalayer.system.status.wifi_task_10s_max_us = 0;
|
||||
datalayer.system.status.mqtt_task_10s_max_us = 0;
|
||||
}
|
||||
#endif // FUNCTION_TIME_MEASUREMENT
|
||||
esp_task_wdt_reset(); // Reset watchdog to prevent reset
|
||||
vTaskDelayUntil(&xLastWakeTime, xFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization functions
|
||||
void init_serial() {
|
||||
// Init Serial monitor
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {}
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("__ OK __");
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
|
||||
void check_interconnect_available() {
|
||||
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
|
||||
return; // Both voltage values need to be available to start check
|
||||
|
@ -601,3 +365,211 @@ void check_reset_reason() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void core_loop(void*) {
|
||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks
|
||||
|
||||
while (true) {
|
||||
|
||||
START_TIME_MEASUREMENT(all);
|
||||
START_TIME_MEASUREMENT(comm);
|
||||
|
||||
monitor_equipment_stop_button();
|
||||
|
||||
// Input, Runs as fast as possible
|
||||
receive_can(); // Receive CAN messages
|
||||
receive_rs485(); // Process serial2 RS485 interface
|
||||
|
||||
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
||||
|
||||
START_TIME_MEASUREMENT(ota);
|
||||
ElegantOTA.loop();
|
||||
END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us);
|
||||
|
||||
// Process
|
||||
currentMillis = millis();
|
||||
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
|
||||
if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) &&
|
||||
(milliseconds(currentMillis) > esp32hal->BOOTUP_TIME())) {
|
||||
set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms));
|
||||
}
|
||||
previousMillis10ms = currentMillis;
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
START_TIME_MEASUREMENT(10ms);
|
||||
}
|
||||
led_exe();
|
||||
handle_contactors(); // Take care of startup precharge/contactor closing
|
||||
if (precharge_control_enabled) {
|
||||
handle_precharge_control(currentMillis); //Drive the hia4v1 via PWM
|
||||
}
|
||||
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
END_TIME_MEASUREMENT_MAX(10ms, datalayer.system.status.time_10ms_us);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMillis - previousMillisUpdateVal >= INTERVAL_1_S) {
|
||||
previousMillisUpdateVal = currentMillis; // Order matters on the update_loop!
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
START_TIME_MEASUREMENT(values);
|
||||
}
|
||||
update_pause_state(); // Check if we are OK to send CAN or need to pause
|
||||
|
||||
// Fetch battery values
|
||||
if (battery) {
|
||||
battery->update_values();
|
||||
}
|
||||
|
||||
if (battery2) {
|
||||
battery2->update_values();
|
||||
check_interconnect_available();
|
||||
}
|
||||
update_calculated_values();
|
||||
update_machineryprotection(); // Check safeties
|
||||
|
||||
// Update values heading towards inverter
|
||||
if (inverter) {
|
||||
inverter->update_values();
|
||||
}
|
||||
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
END_TIME_MEASUREMENT_MAX(values, datalayer.system.status.time_values_us);
|
||||
}
|
||||
}
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
START_TIME_MEASUREMENT(cantx);
|
||||
}
|
||||
|
||||
// Let all transmitter objects send their messages
|
||||
for (auto& transmitter : transmitters) {
|
||||
transmitter->transmit(currentMillis);
|
||||
}
|
||||
|
||||
if (datalayer.system.info.performance_measurement_active) {
|
||||
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
||||
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
||||
if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) {
|
||||
// Update worst case total time
|
||||
datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us;
|
||||
// Record snapshots of task times
|
||||
datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us;
|
||||
datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us;
|
||||
datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us;
|
||||
datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us;
|
||||
datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us;
|
||||
}
|
||||
|
||||
datalayer.system.status.core_task_max_us =
|
||||
MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us);
|
||||
if (core_task_timer_10s.elapsed()) {
|
||||
datalayer.system.status.time_ota_us = 0;
|
||||
datalayer.system.status.time_comm_us = 0;
|
||||
datalayer.system.status.time_10ms_us = 0;
|
||||
datalayer.system.status.time_values_us = 0;
|
||||
datalayer.system.status.time_cantx_us = 0;
|
||||
datalayer.system.status.core_task_10s_max_us = 0;
|
||||
datalayer.system.status.wifi_task_10s_max_us = 0;
|
||||
datalayer.system.status.mqtt_task_10s_max_us = 0;
|
||||
}
|
||||
}
|
||||
esp_task_wdt_reset(); // Reset watchdog to prevent reset
|
||||
vTaskDelayUntil(&xLastWakeTime, xFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
void mqtt_loop(void*) {
|
||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
|
||||
while (true) {
|
||||
START_TIME_MEASUREMENT(mqtt);
|
||||
mqtt_client_loop();
|
||||
END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us);
|
||||
esp_task_wdt_reset(); // Reset watchdog
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization
|
||||
void setup() {
|
||||
init_hal();
|
||||
|
||||
init_serial();
|
||||
|
||||
// We print this after setting up serial, so that is also printed if configured to do so
|
||||
DEBUG_PRINTF("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number);
|
||||
|
||||
init_events();
|
||||
|
||||
init_stored_settings();
|
||||
|
||||
if (wifi_enabled) {
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&connectivity_loop, "connectivity_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
|
||||
&connectivity_loop_task, esp32hal->WIFICORE());
|
||||
}
|
||||
|
||||
led_init();
|
||||
|
||||
if (datalayer.system.info.CAN_SD_logging_active || datalayer.system.info.SD_logging_active) {
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
|
||||
&logging_loop_task, esp32hal->WIFICORE());
|
||||
}
|
||||
|
||||
init_contactors();
|
||||
|
||||
init_precharge_control();
|
||||
|
||||
setup_charger();
|
||||
setup_inverter();
|
||||
setup_battery();
|
||||
setup_can_shunt();
|
||||
|
||||
// Init CAN only after any CAN receivers have had a chance to register.
|
||||
init_CAN();
|
||||
|
||||
init_rs485();
|
||||
|
||||
init_equipment_stop_button();
|
||||
|
||||
// BOOT button at runtime is used as an input for various things
|
||||
pinMode(0, INPUT_PULLUP);
|
||||
|
||||
check_reset_reason();
|
||||
|
||||
// Initialize Task Watchdog for subscribed tasks
|
||||
esp_task_wdt_config_t wdt_config = {// 5s should be enough for the connectivity tasks (which are all contending
|
||||
// for the same core) to yield to each other and reset their watchdogs.
|
||||
.timeout_ms = INTERVAL_5_S,
|
||||
// We don't benefit from idle task watchdogs, our critical loops have their
|
||||
// own. The idle watchdogs can cause nuisance reboots under heavy load.
|
||||
.idle_core_mask = 0,
|
||||
// Panic (and reboot) on timeout
|
||||
.trigger_panic = true};
|
||||
#ifdef CONFIG_ESP_TASK_WDT
|
||||
// ESP-IDF will have already initialized it, so reconfigure.
|
||||
// Arduino and PlatformIO have different watchdog defaults, so we reconfigure
|
||||
// for consistency.
|
||||
esp_task_wdt_reconfigure(&wdt_config);
|
||||
#else
|
||||
// Otherwise initialize it for the first time.
|
||||
esp_task_wdt_init(&wdt_config);
|
||||
#endif
|
||||
|
||||
// Start tasks
|
||||
|
||||
if (mqtt_enabled) {
|
||||
init_mqtt();
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
|
||||
esp32hal->WIFICORE());
|
||||
}
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,
|
||||
esp32hal->CORE_FUNCTION_CORE());
|
||||
|
||||
DEBUG_PRINTF("Setup complete!\n");
|
||||
}
|
||||
|
||||
// Loop empty, all functionality runs in tasks
|
||||
void loop() {}
|
|
@ -1,22 +0,0 @@
|
|||
/* This file should be renamed to USER_SECRETS.h to be able to use the software!
|
||||
It contains all the credentials that should never be made public */
|
||||
#ifndef COMMON_IMAGE
|
||||
|
||||
//Password to the access point generated by the Battery-Emulator
|
||||
#define AP_PASSWORD "123456789" // Minimum of 8 characters; set to blank if you want the access point to be open
|
||||
|
||||
//Name and password of Wifi network you want the emulator to connect to
|
||||
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID" // Maximum of 63 characters
|
||||
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD" // Minimum of 8 characters
|
||||
|
||||
//Set WEBSERVER_AUTH_REQUIRED to true to require a password when accessing the webserver homepage. Improves cybersecurity.
|
||||
#define WEBSERVER_AUTH_REQUIRED false
|
||||
#define HTTP_USERNAME "admin" // Username for webserver authentication
|
||||
#define HTTP_PASSWORD "admin" // Password for webserver authentication
|
||||
|
||||
//MQTT credentials
|
||||
#define MQTT_SERVER "192.168.xxx.yyy" // MQTT server address
|
||||
#define MQTT_PORT 1883 // MQTT server port
|
||||
#define MQTT_USER "" // MQTT username, leave blank for no authentication
|
||||
#define MQTT_PASSWORD "" // MQTT password, leave blank for no authentication
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
#include "USER_SETTINGS.h"
|
||||
#include <string>
|
||||
#include "USER_SECRETS.h"
|
||||
#include "src/devboard/hal/hal.h"
|
||||
|
||||
/* This file contains all the battery settings and limits */
|
||||
/* They can be defined here, or later on in the WebUI */
|
||||
/* Most important is to select which CAN interface each component is connected to */
|
||||
/*
|
||||
CAN_NATIVE = Native CAN port on the LilyGo & Stark hardware
|
||||
CANFD_NATIVE = Native CANFD port on the Stark CMR hardware
|
||||
CAN_ADDON_MCP2515 = Add-on CAN MCP2515 connected to GPIO pins
|
||||
CANFD_ADDON_MCP2518 = Add-on CAN-FD MCP2518 connected to GPIO pins
|
||||
*/
|
||||
|
||||
volatile CAN_Configuration can_config = {
|
||||
.battery = CAN_NATIVE, // Which CAN is your battery connected to?
|
||||
.inverter = CAN_NATIVE, // Which CAN is your inverter connected to? (No need to configure incase you use RS485)
|
||||
.battery_double = CAN_ADDON_MCP2515, // (OPTIONAL) Which CAN is your second battery connected to?
|
||||
.charger = CAN_NATIVE, // (OPTIONAL) Which CAN is your charger connected to?
|
||||
.shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to?
|
||||
};
|
||||
|
||||
#ifdef COMMON_IMAGE
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
std::string passwordAP;
|
||||
#else
|
||||
std::string ssid = WIFI_SSID; // Set in USER_SECRETS.h
|
||||
std::string password = WIFI_PASSWORD; // Set in USER_SECRETS.h
|
||||
std::string passwordAP = AP_PASSWORD; // Set in USER_SECRETS.h
|
||||
#endif
|
||||
|
||||
const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection
|
||||
|
||||
#ifdef COMMON_IMAGE
|
||||
std::string http_username;
|
||||
std::string http_password;
|
||||
#else
|
||||
std::string http_username = HTTP_USERNAME; // Set in USER_SECRETS.h
|
||||
std::string http_password = HTTP_PASSWORD; // Set in USER_SECRETS.h
|
||||
#endif
|
||||
|
||||
// Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h
|
||||
IPAddress local_IP(192, 168, 10, 150);
|
||||
IPAddress gateway(192, 168, 10, 1);
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
|
||||
// MQTT
|
||||
#ifdef COMMON_IMAGE
|
||||
std::string mqtt_user;
|
||||
std::string mqtt_password;
|
||||
#else
|
||||
std::string mqtt_user = MQTT_USER; // Set in USER_SECRETS.h
|
||||
std::string mqtt_password = MQTT_PASSWORD; // Set in USER_SECRETS.h
|
||||
#endif
|
||||
|
||||
const char* mqtt_topic_name =
|
||||
"BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX"
|
||||
const char* mqtt_object_id_prefix =
|
||||
"be_"; // Custom prefix for MQTT object ID. Previously, the prefix was automatically set to "esp32-XXXXXX_"
|
||||
const char* mqtt_device_name =
|
||||
"Battery Emulator"; // Custom device name in Home Assistant. Previously, the name was automatically set to "BatteryEmulator_esp32-XXXXXX"
|
||||
const char* ha_device_id =
|
||||
"battery-emulator"; // Custom device ID in Home Assistant. Previously, the ID was always "battery-emulator"
|
||||
|
||||
/* Charger settings (Optional, when using generator charging) */
|
||||
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
|
||||
volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger
|
||||
volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger
|
||||
volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config
|
||||
volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger
|
||||
volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete
|
||||
|
||||
#ifdef PERIODIC_BMS_RESET_AT
|
||||
// A list of rules for your zone can be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
|
||||
const char* time_zone =
|
||||
"GMT0BST,M3.5.0/1,M10.5.0"; // TimeZone rule for Europe/London including daylight adjustment rules (optional)
|
||||
const char* ntpServer1 = "pool.ntp.org";
|
||||
const char* ntpServer2 = "time.nist.gov";
|
||||
#endif
|
|
@ -1,229 +0,0 @@
|
|||
#ifndef __USER_SETTINGS_H__
|
||||
#define __USER_SETTINGS_H__
|
||||
#include <WiFi.h>
|
||||
#include <stdint.h>
|
||||
#include "src/devboard/utils/types.h"
|
||||
|
||||
/* This file contains all the battery/inverter protocol settings Battery-Emulator software */
|
||||
/* To switch between batteries/inverters, uncomment a line to enable, comment out to disable. */
|
||||
/* There are also some options for battery limits and extra functionality */
|
||||
/* To edit battery specific limits, see also the USER_SETTINGS.cpp file*/
|
||||
|
||||
/* Select battery used */
|
||||
//#define BMW_I3_BATTERY
|
||||
//#define BMW_IX_BATTERY
|
||||
//#define BMW_PHEV_BATTERY
|
||||
//#define BOLT_AMPERA_BATTERY
|
||||
//#define BYD_ATTO_3_BATTERY
|
||||
//#define FOXESS_BATTERY
|
||||
//#define CELLPOWER_BMS
|
||||
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
|
||||
//#define GEELY_GEOMETRY_C_BATTERY
|
||||
//#define HYUNDAI_IONIQ_28_BATTERY
|
||||
//#define CMFA_EV_BATTERY
|
||||
//#define IMIEV_CZERO_ION_BATTERY
|
||||
//#define JAGUAR_IPACE_BATTERY
|
||||
//#define KIA_E_GMP_BATTERY
|
||||
//#define KIA_HYUNDAI_64_BATTERY
|
||||
//#define KIA_HYUNDAI_HYBRID_BATTERY
|
||||
//#define MEB_BATTERY
|
||||
//#define MG_5_BATTERY
|
||||
//#define MG_HS_PHEV_BATTERY
|
||||
//#define NISSAN_LEAF_BATTERY
|
||||
//#define ORION_BMS
|
||||
//#define PYLON_BATTERY
|
||||
//#define DALY_BMS
|
||||
//#define RJXZS_BMS
|
||||
//#define RANGE_ROVER_PHEV_BATTERY
|
||||
//#define RENAULT_KANGOO_BATTERY
|
||||
//#define RENAULT_TWIZY_BATTERY
|
||||
//#define RENAULT_ZOE_GEN1_BATTERY
|
||||
//#define RENAULT_ZOE_GEN2_BATTERY
|
||||
//#define SONO_BATTERY
|
||||
//#define SAMSUNG_SDI_LV_BATTERY
|
||||
//#define SANTA_FE_PHEV_BATTERY
|
||||
//#define SIMPBMS_BATTERY
|
||||
//#define STELLANTIS_ECMP_BATTERY
|
||||
//#define TESLA_MODEL_3Y_BATTERY
|
||||
//#define TESLA_MODEL_SX_BATTERY
|
||||
//#define VOLVO_SPA_BATTERY
|
||||
//#define VOLVO_SPA_HYBRID_BATTERY
|
||||
//#define TEST_FAKE_BATTERY
|
||||
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires separate CAN setup)
|
||||
|
||||
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/Battery-Emulator/wiki */
|
||||
//#define AFORE_CAN //Enable this line to emulate an "Afore battery" over CAN bus
|
||||
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
|
||||
//#define BYD_CAN_DEYE //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus, with Deye specific fixes
|
||||
//#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485
|
||||
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
|
||||
//#define FERROAMP_CAN //Enable this line to emulate a "Pylon 4x96V Force H2" over CAN Bus
|
||||
//#define FOXESS_CAN //Enable this line to emulate a "HV2600/ECS4100 battery" over CAN bus
|
||||
//#define GROWATT_HV_CAN //Enable this line to emulate a "Growatt High Voltage v1.10 battery" over CAN bus
|
||||
//#define GROWATT_LV_CAN //Enable this line to emulate a "48V Growatt Low Voltage battery" over CAN bus
|
||||
//#define PYLON_LV_CAN //Enable this line to emulate a "48V Pylontech battery" over CAN bus
|
||||
//#define PYLON_CAN //Enable this line to emulate a "High Voltage Pylontech battery" over CAN bus
|
||||
//#define SCHNEIDER_CAN //Enable this line to emulate a "Schneider Version 2: SE BMS" over CAN bus
|
||||
//#define SMA_BYD_H_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" (SMA compatible) over CAN bus
|
||||
//#define SMA_BYD_HVS_CAN //Enable this line to emulate a "BYD Battery-Box HVS 10.2KW battery" (SMA compatible) over CAN bus
|
||||
//#define SMA_LV_CAN //Enable this line to emulate a "SMA Sunny Island 48V battery" over CAN bus
|
||||
//#define SMA_TRIPOWER_CAN //Enable this line to emulate a "SMA Home Storage battery" over CAN bus
|
||||
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
|
||||
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
||||
//#define SOLXPOW_CAN //Enable this line to emulate a "Solxpow compatible battery" over CAN bus
|
||||
//#define SOL_ARK_LV_CAN //Enable this line to emulate a "Sol-Ark compatible LV battery" over CAN bus
|
||||
//#define SUNGROW_CAN //Enable this line to emulate a "Sungrow SBR064" over CAN bus
|
||||
|
||||
/* Select hardware used for Battery-Emulator */
|
||||
//#define HW_LILYGO
|
||||
//#define HW_STARK
|
||||
//#define HW_3LB
|
||||
//#define HW_DEVKIT
|
||||
|
||||
/* Contactor settings. If you have a battery that does not activate contactors via CAN, configure this section */
|
||||
#define PRECHARGE_TIME_MS 500 //Precharge time in milliseconds. Modify to suit your inverter (See wiki for more info)
|
||||
//#define CONTACTOR_CONTROL //Enable this line to have the emulator handle automatic precharge/contactor+/contactor- closing sequence (See wiki for pins)
|
||||
//#define CONTACTOR_CONTROL_DOUBLE_BATTERY //Enable this line to have the emulator hardware control secondary set of contactors for double battery setups (See wiki for pins)
|
||||
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled.
|
||||
//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting!
|
||||
//#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF
|
||||
//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF
|
||||
// PERIODIC_BMS_RESET_AT Uses NTP server, internet required. In 24 Hour format WITHOUT leading 0. e.g 0230 should be 230. Time Zone is set in USER_SETTINGS.cpp
|
||||
//#define PERIODIC_BMS_RESET_AT 525
|
||||
|
||||
/* Shunt/Contactor settings (Optional) */
|
||||
//#define BMW_SBOX // SBOX relay control & battery current/voltage measurement
|
||||
|
||||
/* Select charger used (Optional) */
|
||||
//#define CHEVYVOLT_CHARGER //Enable this line to control a Chevrolet Volt charger connected to battery - for example, when generator charging or using an inverter without a charging function.
|
||||
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
|
||||
|
||||
/* Automatic Precharge settings (Optional) If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */
|
||||
//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 via PWM on the HIA4V1_PIN (see Wiki and HAL for pin definition)
|
||||
//#define INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN //Enable this line if you use a normally open contactor instead of normally closed
|
||||
|
||||
/* Other options */
|
||||
//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery
|
||||
//#define LFP_CHEMISTRY //Tesla specific setting, enable this line to startup in LFP mode
|
||||
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production)
|
||||
//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (WARNING, raises CPU load, do not use for production)
|
||||
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production)
|
||||
//#define DEBUG_VIA_WEB //Enable this line to log diagnostic data while program runs, which can be viewed via webpage (WARNING, slightly raises CPU load, do not use for production)
|
||||
//#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production)
|
||||
|
||||
/* CAN options */
|
||||
//#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
|
||||
#define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency?
|
||||
//#define CANFD_ADDON //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
|
||||
#define CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ \
|
||||
ACAN2517FDSettings::OSC_40MHz //CANFD_ADDON option, what is your MCP2518 add-on boards crystal frequency?
|
||||
//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN
|
||||
|
||||
/* Connectivity options */
|
||||
#define WIFI
|
||||
//#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings
|
||||
//#define CUSTOM_HOSTNAME \
|
||||
"battery-emulator" //Enable this line to use a custom hostname for the device, if disabled the default naming format 'esp32-XXXXXX' will be used.
|
||||
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
||||
#define WIFIAP //When enabled, the emulator will broadcast its own access point Wifi. Can be used at the same time as a normal Wifi connection to a router.
|
||||
#define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled.
|
||||
#define LOAD_SAVED_SETTINGS_ON_BOOT // Enable this line to read settings stored via the webserver on boot (overrides Wifi credentials set here)
|
||||
//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production)
|
||||
|
||||
/* MQTT options */
|
||||
// #define MQTT // Enable this line to enable MQTT
|
||||
#define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2)
|
||||
#define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT
|
||||
#define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds
|
||||
#define MQTT_MANUAL_TOPIC_OBJECT_NAME
|
||||
// Enable MQTT_MANUAL_TOPIC_OBJECT_NAME to use custom MQTT topic, object ID prefix, and device name.
|
||||
// WARNING: If this is not defined, the previous default naming format 'battery-emulator_esp32-XXXXXX' (based on hardware ID) will be used.
|
||||
// This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change
|
||||
// may break compatibility with previous versions of MQTT naming. Please refer to USER_SETTINGS.cpp for configuration options.
|
||||
|
||||
/* Home Assistant options */
|
||||
#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required
|
||||
|
||||
/* Battery settings */
|
||||
// Predefined total energy capacity of the battery in Watt-hours (updates automatically from battery data when available)
|
||||
#define BATTERY_WH_MAX 30000
|
||||
// Increases battery life. If true will rescale SOC between the configured min/max-percentage
|
||||
#define BATTERY_USE_SCALED_SOC true
|
||||
// 8000 = 80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached)
|
||||
#define BATTERY_MAXPERCENTAGE 8000
|
||||
// 2000 = 20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached)
|
||||
#define BATTERY_MINPERCENTAGE 2000
|
||||
// 500 = 50.0 °C , Max temperature (Will produce a battery overheat event if above)
|
||||
#define BATTERY_MAXTEMPERATURE 500
|
||||
// -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below)
|
||||
#define BATTERY_MINTEMPERATURE -250
|
||||
// 150 = 15.0 °C , Max difference between min and max temperature (Will produce a battery temperature deviation event if greater)
|
||||
#define BATTERY_MAX_TEMPERATURE_DEVIATION 150
|
||||
// 300 = 30.0A , Max charge in Amp (Some inverters needs to be limited)
|
||||
#define BATTERY_MAX_CHARGE_AMP 300
|
||||
// 300 = 30.0A , Max discharge in Amp (Some inverters needs to be limited)
|
||||
#define BATTERY_MAX_DISCHARGE_AMP 300
|
||||
// Enable this to manually set voltage limits on how much battery can be discharged/charged. Normally not used.
|
||||
#define BATTERY_USE_VOLTAGE_LIMITS false
|
||||
// 5000 = 500.0V , Target charge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true
|
||||
#define BATTERY_MAX_CHARGE_VOLTAGE 5000
|
||||
// 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true
|
||||
#define BATTERY_MAX_DISCHARGE_VOLTAGE 3000
|
||||
|
||||
/* LED settings. Optional customization for how the blinking pattern on the LED should behave.
|
||||
* CLASSIC - Slow up/down ramp. If CLASSIC, then a ramp up and ramp down will finish in LED_PERIOD_MS milliseconds
|
||||
* FLOW - Ramp up/down depending on flow of energy
|
||||
* HEARTBEAT - Heartbeat-like LED pattern that reacts to the system state with color and BPM
|
||||
*/
|
||||
#define LED_MODE CLASSIC
|
||||
#define LED_PERIOD_MS 3000
|
||||
|
||||
/* Do not change any code below this line */
|
||||
/* Only change battery specific settings above and in "USER_SETTINGS.cpp" */
|
||||
typedef struct {
|
||||
CAN_Interface battery;
|
||||
CAN_Interface inverter;
|
||||
CAN_Interface battery_double;
|
||||
CAN_Interface charger;
|
||||
CAN_Interface shunt;
|
||||
} CAN_Configuration;
|
||||
extern volatile CAN_Configuration can_config;
|
||||
extern volatile uint8_t AccessPointEnabled;
|
||||
extern const uint8_t wifi_channel;
|
||||
extern volatile float CHARGER_SET_HV;
|
||||
extern volatile float CHARGER_MAX_HV;
|
||||
extern volatile float CHARGER_MIN_HV;
|
||||
extern volatile float CHARGER_MAX_POWER;
|
||||
extern volatile float CHARGER_MAX_A;
|
||||
extern volatile float CHARGER_END_A;
|
||||
|
||||
extern volatile unsigned long long bmsResetTimeOffset;
|
||||
|
||||
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
|
||||
|
||||
// Equipment stop button behavior. Use NC button for safety reasons.
|
||||
//LATCHING_SWITCH - Normally closed (NC), latching switch. When pressed it activates e-stop
|
||||
//MOMENTARY_SWITCH - Short press to activate e-stop, long 15s press to deactivate. E-stop is persistent between reboots
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH;
|
||||
#else
|
||||
const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::NOT_CONNECTED;
|
||||
#endif
|
||||
|
||||
#ifdef WIFICONFIG
|
||||
extern IPAddress local_IP;
|
||||
extern IPAddress gateway;
|
||||
extern IPAddress subnet;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_VIA_USB) || defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
|
||||
#define DEBUG_LOG
|
||||
#endif
|
||||
|
||||
#if defined(MEB_BATTERY)
|
||||
#define PRECHARGE_CONTROL
|
||||
#endif
|
||||
|
||||
#endif // __USER_SETTINGS_H__
|
|
@ -3,10 +3,6 @@
|
|||
#include "CanBattery.h"
|
||||
#include "RS485Battery.h"
|
||||
|
||||
#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS)
|
||||
#error No battery selected! Choose one from the USER_SETTINGS.h file or build COMMON_IMAGE.
|
||||
#endif
|
||||
|
||||
Battery* battery = nullptr;
|
||||
Battery* battery2 = nullptr;
|
||||
|
||||
|
@ -22,6 +18,8 @@ std::vector<BatteryType> supported_battery_types() {
|
|||
|
||||
const char* name_for_chemistry(battery_chemistry_enum chem) {
|
||||
switch (chem) {
|
||||
case battery_chemistry_enum::Autodetect:
|
||||
return "Autodetect";
|
||||
case battery_chemistry_enum::LFP:
|
||||
return "LFP";
|
||||
case battery_chemistry_enum::NCA:
|
||||
|
@ -90,6 +88,8 @@ const char* name_for_battery_type(BatteryType type) {
|
|||
return KiaEGmpBattery::Name;
|
||||
case BatteryType::KiaHyundai64:
|
||||
return KiaHyundai64Battery::Name;
|
||||
case BatteryType::Kia64FD:
|
||||
return Kia64FDBattery::Name;
|
||||
case BatteryType::KiaHyundaiHybrid:
|
||||
return KiaHyundaiHybridBattery::Name;
|
||||
case BatteryType::Meb:
|
||||
|
@ -108,6 +108,8 @@ const char* name_for_battery_type(BatteryType type) {
|
|||
return RjxzsBms::Name;
|
||||
case BatteryType::RangeRoverPhev:
|
||||
return RangeRoverPhevBattery::Name;
|
||||
case BatteryType::RelionBattery:
|
||||
return RelionBattery::Name;
|
||||
case BatteryType::RenaultKangoo:
|
||||
return RenaultKangooBattery::Name;
|
||||
case BatteryType::RenaultTwizy:
|
||||
|
@ -116,6 +118,8 @@ const char* name_for_battery_type(BatteryType type) {
|
|||
return RenaultZoeGen1Battery::Name;
|
||||
case BatteryType::RenaultZoe2:
|
||||
return RenaultZoeGen2Battery::Name;
|
||||
case BatteryType::RivianBattery:
|
||||
return RivianBattery::Name;
|
||||
case BatteryType::SamsungSdiLv:
|
||||
return SamsungSdiLVBattery::Name;
|
||||
case BatteryType::SantaFePhev:
|
||||
|
@ -137,19 +141,10 @@ const char* name_for_battery_type(BatteryType type) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef LFP_CHEMISTRY
|
||||
const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::LFP;
|
||||
#else
|
||||
const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::NMC;
|
||||
#endif
|
||||
|
||||
battery_chemistry_enum user_selected_battery_chemistry = battery_chemistry_default;
|
||||
|
||||
#ifdef COMMON_IMAGE
|
||||
#ifdef SELECTED_BATTERY_CLASS
|
||||
#error "Compile time SELECTED_BATTERY_CLASS should not be defined with COMMON_IMAGE"
|
||||
#endif
|
||||
|
||||
BatteryType user_selected_battery_type = BatteryType::NissanLeaf;
|
||||
bool user_selected_second_battery = false;
|
||||
|
||||
|
@ -187,6 +182,8 @@ Battery* create_battery(BatteryType type) {
|
|||
return new ImievCZeroIonBattery();
|
||||
case BatteryType::JaguarIpace:
|
||||
return new JaguarIpaceBattery();
|
||||
case BatteryType::Kia64FD:
|
||||
return new Kia64FDBattery();
|
||||
case BatteryType::KiaEGmp:
|
||||
return new KiaEGmpBattery();
|
||||
case BatteryType::KiaHyundai64:
|
||||
|
@ -209,6 +206,8 @@ Battery* create_battery(BatteryType type) {
|
|||
return new RjxzsBms();
|
||||
case BatteryType::RangeRoverPhev:
|
||||
return new RangeRoverPhevBattery();
|
||||
case BatteryType::RelionBattery:
|
||||
return new RelionBattery();
|
||||
case BatteryType::RenaultKangoo:
|
||||
return new RenaultKangooBattery();
|
||||
case BatteryType::RenaultTwizy:
|
||||
|
@ -217,6 +216,8 @@ Battery* create_battery(BatteryType type) {
|
|||
return new RenaultZoeGen1Battery();
|
||||
case BatteryType::RenaultZoe2:
|
||||
return new RenaultZoeGen2Battery();
|
||||
case BatteryType::RivianBattery:
|
||||
return new RivianBattery();
|
||||
case BatteryType::SamsungSdiLv:
|
||||
return new SamsungSdiLVBattery();
|
||||
case BatteryType::SantaFePhev:
|
||||
|
@ -269,9 +270,15 @@ void setup_battery() {
|
|||
case BatteryType::RenaultZoe1:
|
||||
battery2 = new RenaultZoeGen1Battery(&datalayer.battery2, nullptr, can_config.battery_double);
|
||||
break;
|
||||
case BatteryType::RenaultZoe2:
|
||||
battery2 = new RenaultZoeGen2Battery(&datalayer.battery2, nullptr, can_config.battery_double);
|
||||
break;
|
||||
case BatteryType::TestFake:
|
||||
battery2 = new TestFakeBattery(&datalayer.battery2, can_config.battery_double);
|
||||
break;
|
||||
default:
|
||||
DEBUG_PRINTF("User tried enabling double battery on non-supported integration!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (battery2) {
|
||||
|
@ -279,36 +286,21 @@ void setup_battery() {
|
|||
}
|
||||
}
|
||||
}
|
||||
#else // Battery selection has been made at build-time
|
||||
|
||||
void setup_battery() {
|
||||
// Instantiate the battery only once just in case this function gets called multiple times.
|
||||
if (battery == nullptr) {
|
||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||
battery = new SELECTED_BATTERY_CLASS(user_selected_battery_chemistry);
|
||||
#else
|
||||
battery = new SELECTED_BATTERY_CLASS();
|
||||
#endif
|
||||
}
|
||||
battery->setup();
|
||||
/* User-selected Nissan LEAF settings */
|
||||
bool user_selected_LEAF_interlock_mandatory = false;
|
||||
/* User-selected Tesla settings */
|
||||
bool user_selected_tesla_digital_HVIL = false;
|
||||
uint16_t user_selected_tesla_GTW_country = 17477;
|
||||
bool user_selected_tesla_GTW_rightHandDrive = true;
|
||||
uint16_t user_selected_tesla_GTW_mapRegion = 2;
|
||||
uint16_t user_selected_tesla_GTW_chassisType = 2;
|
||||
uint16_t user_selected_tesla_GTW_packEnergy = 1;
|
||||
/* User-selected EGMP+others settings */
|
||||
bool user_selected_use_estimated_SOC = false;
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
if (battery2 == nullptr) {
|
||||
#if defined(BMW_I3_BATTERY)
|
||||
battery2 =
|
||||
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
|
||||
can_config.battery_double, esp32hal->WUP_PIN2());
|
||||
#elif defined(KIA_HYUNDAI_64_BATTERY)
|
||||
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2,
|
||||
&datalayer.system.status.battery2_allowed_contactor_closing,
|
||||
can_config.battery_double);
|
||||
#elif defined(SANTA_FE_PHEV_BATTERY) || defined(TEST_FAKE_BATTERY)
|
||||
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, can_config.battery_double);
|
||||
#else
|
||||
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, nullptr, can_config.battery_double);
|
||||
#endif
|
||||
}
|
||||
battery2->setup();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
// Use 0V for user selected cell/pack voltage defaults (On boot will be replaced with saved values from NVM)
|
||||
uint16_t user_selected_max_pack_voltage_dV = 0;
|
||||
uint16_t user_selected_min_pack_voltage_dV = 0;
|
||||
uint16_t user_selected_max_cell_voltage_mV = 0;
|
||||
uint16_t user_selected_min_cell_voltage_mV = 0;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#ifndef BATTERIES_H
|
||||
#define BATTERIES_H
|
||||
#include "../../USER_SETTINGS.h"
|
||||
#include "Shunt.h"
|
||||
|
||||
class Battery;
|
||||
|
@ -29,6 +28,7 @@ void setup_can_shunt();
|
|||
#include "HYUNDAI-IONIQ-28-BATTERY.h"
|
||||
#include "IMIEV-CZERO-ION-BATTERY.h"
|
||||
#include "JAGUAR-IPACE-BATTERY.h"
|
||||
#include "KIA-64FD-BATTERY.h"
|
||||
#include "KIA-E-GMP-BATTERY.h"
|
||||
#include "KIA-HYUNDAI-64-BATTERY.h"
|
||||
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
|
||||
|
@ -39,10 +39,12 @@ void setup_can_shunt();
|
|||
#include "ORION-BMS.h"
|
||||
#include "PYLON-BATTERY.h"
|
||||
#include "RANGE-ROVER-PHEV-BATTERY.h"
|
||||
#include "RELION-LV-BATTERY.h"
|
||||
#include "RENAULT-KANGOO-BATTERY.h"
|
||||
#include "RENAULT-TWIZY.h"
|
||||
#include "RENAULT-ZOE-GEN1-BATTERY.h"
|
||||
#include "RENAULT-ZOE-GEN2-BATTERY.h"
|
||||
#include "RIVIAN-BATTERY.h"
|
||||
#include "RJXZS-BMS.h"
|
||||
#include "SAMSUNG-SDI-LV-BATTERY.h"
|
||||
#include "SANTA-FE-PHEV-BATTERY.h"
|
||||
|
@ -54,5 +56,19 @@ void setup_can_shunt();
|
|||
#include "VOLVO-SPA-HYBRID-BATTERY.h"
|
||||
|
||||
void setup_battery(void);
|
||||
Battery* create_battery(BatteryType type);
|
||||
|
||||
extern uint16_t user_selected_max_pack_voltage_dV;
|
||||
extern uint16_t user_selected_min_pack_voltage_dV;
|
||||
extern uint16_t user_selected_max_cell_voltage_mV;
|
||||
extern uint16_t user_selected_min_cell_voltage_mV;
|
||||
extern bool user_selected_use_estimated_SOC;
|
||||
extern bool user_selected_LEAF_interlock_mandatory;
|
||||
extern bool user_selected_tesla_digital_HVIL;
|
||||
extern uint16_t user_selected_tesla_GTW_country;
|
||||
extern bool user_selected_tesla_GTW_rightHandDrive;
|
||||
extern uint16_t user_selected_tesla_GTW_mapRegion;
|
||||
extern uint16_t user_selected_tesla_GTW_chassisType;
|
||||
extern uint16_t user_selected_tesla_GTW_packEnergy;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
#include "BMW-I3-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef BMW_I3_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS BmwI3Battery
|
||||
#endif
|
||||
|
||||
class BmwI3Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
|
|
@ -37,7 +37,7 @@ uint8_t BmwIXBattery::increment_alive_counter(uint8_t counter) {
|
|||
return counter;
|
||||
}
|
||||
|
||||
static byte increment_C0_counter(byte counter) {
|
||||
static uint8_t increment_C0_counter(uint8_t counter) {
|
||||
counter++;
|
||||
// Reset to 0xF0 if it exceeds 0xFE
|
||||
if (counter > 0xFE) {
|
||||
|
@ -60,7 +60,7 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche
|
|||
|
||||
datalayer.battery.status.soh_pptt = min_soh_state;
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W;
|
||||
datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W;
|
||||
|
||||
//datalayer.battery.status.max_charge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse Moved to Ramping
|
||||
|
||||
|
@ -70,10 +70,10 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche
|
|||
} else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) {
|
||||
// When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
MAX_CHARGE_POWER_ALLOWED_W *
|
||||
datalayer.battery.status.override_charge_power_W *
|
||||
(1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
|
||||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W;
|
||||
datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W;
|
||||
}
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = min_battery_temperature;
|
||||
|
@ -307,9 +307,7 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
|
||||
if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 ||
|
||||
(rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
|
||||
#endif // DEBUG_LOG
|
||||
//set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET);
|
||||
} else { //Only ingest values if they are not the 10V Error state
|
||||
|
@ -378,15 +376,11 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
|
|||
// Detect edge
|
||||
if (ContactorCloseRequest.previous == false && ContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Rising edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
} else if (ContactorCloseRequest.previous == true && ContactorCloseRequest.present == false) {
|
||||
// Dropping edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dropping edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
}
|
||||
ContactorCloseRequest.previous = ContactorCloseRequest.present;
|
||||
|
@ -465,12 +459,12 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup
|
|||
|
||||
void BmwIXBattery::HandleIncomingUserRequest(void) {
|
||||
// Debug user request to open or close the contactors
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("User request: contactor close: ");
|
||||
logging.print(userRequestContactorClose);
|
||||
logging.print(" User request: contactor open: ");
|
||||
logging.println(userRequestContactorOpen);
|
||||
#endif // DEBUG_LOG
|
||||
if (userRequestContactorClose) {
|
||||
logging.printf("User request: contactor close");
|
||||
}
|
||||
if (userRequestContactorOpen) {
|
||||
logging.printf("User request: contactor open");
|
||||
}
|
||||
if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) {
|
||||
// do nothing
|
||||
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) {
|
||||
|
@ -487,11 +481,9 @@ void BmwIXBattery::HandleIncomingUserRequest(void) {
|
|||
// set user request to false
|
||||
userRequestContactorClose = false;
|
||||
userRequestContactorOpen = false;
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
#ifdef DEBUG_LOG
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
logging.println(
|
||||
"Error: user requested contactors to close and open at the same time. Contactors have been opened.");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,16 +491,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) {
|
|||
InverterContactorCloseRequest.present = datalayer.system.status.inverter_allows_contactor_closing;
|
||||
// Detect edge
|
||||
if (InverterContactorCloseRequest.previous == false && InverterContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
// Rising edge detected
|
||||
logging.println("Inverter requests to close contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxCloseContactors();
|
||||
} else if (InverterContactorCloseRequest.previous == true && InverterContactorCloseRequest.present == false) {
|
||||
// Falling edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
// Falling edge detected
|
||||
logging.println("Inverter requests to open contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxOpenContactors();
|
||||
} // else: do nothing
|
||||
|
||||
|
@ -517,16 +505,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) {
|
|||
}
|
||||
|
||||
void BmwIXBattery::BmwIxCloseContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Closing contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = true;
|
||||
}
|
||||
|
||||
void BmwIXBattery::BmwIxOpenContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Opening contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = false;
|
||||
counter_100ms = 0; // reset counter, such that keep contactors closed message sequence starts from the beginning
|
||||
}
|
||||
|
@ -552,46 +536,34 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
|
|||
if (counter_10ms == 0) {
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 1/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 5) {
|
||||
// @50 ms
|
||||
transmit_can_frame(&BMWiX_276);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x276 - 2/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 10) {
|
||||
// @100 ms
|
||||
BMWiX_510.data.u8[2] = 0x04; // TODO: check if needed
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 3/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 20) {
|
||||
// @200 ms
|
||||
BMWiX_510.data.u8[2] = 0x10; // TODO: check if needed
|
||||
BMWiX_510.data.u8[5] = 0x80; // needed to close contactors
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 4/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 30) {
|
||||
// @300 ms
|
||||
BMWiX_16E.data.u8[0] = 0x6A;
|
||||
BMWiX_16E.data.u8[1] = 0xAD;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 5/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 50) {
|
||||
// @500 ms
|
||||
BMWiX_16E.data.u8[0] = 0x03;
|
||||
BMWiX_16E.data.u8[1] = 0xA9;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 6/6");
|
||||
#endif // DEBUG_LOG
|
||||
ContactorState.closed = true;
|
||||
ContactorState.open = false;
|
||||
}
|
||||
|
@ -613,9 +585,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
|
|||
0xC9, 0x3A, 0xF7}; // Explicit declaration, to prevent modification by other functions
|
||||
|
||||
if (counter_100ms == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages started");
|
||||
#endif // DEBUG_LOG
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
} else if (counter_100ms == 7) {
|
||||
|
@ -631,9 +601,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
|
|||
BMWiX_16E.data.u8[0] = 0x02;
|
||||
BMWiX_16E.data.u8[1] = 0xA7;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages finished");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_100ms == 140) {
|
||||
// @14000 ms
|
||||
// reset counter (outside of this function)
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "BMW-IX-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef BMW_IX_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS BmwIXBattery
|
||||
#endif
|
||||
|
||||
class BmwIXBattery : public CanBattery {
|
||||
public:
|
||||
BmwIXBattery() : renderer(*this) {}
|
||||
|
@ -52,8 +48,6 @@ class BmwIXBattery : public CanBattery {
|
|||
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4300; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2800; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000;
|
||||
static const int MAX_CHARGE_POWER_ALLOWED_W = 10000;
|
||||
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
|
||||
static const int RAMPDOWN_SOC =
|
||||
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "BMW-PHEV-BATTERY.h"
|
||||
#include <Arduino.h>
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
|
@ -122,9 +123,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) {
|
|||
if (gUDSContext.UDS_bytesReceived + length > sizeof(gUDSContext.UDS_buffer)) {
|
||||
// Overflow => abort
|
||||
gUDSContext.UDS_inProgress = false;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UDS Payload Overflow");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
memcpy(&gUDSContext.UDS_buffer[gUDSContext.UDS_bytesReceived], payload, length);
|
||||
|
@ -134,9 +133,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) {
|
|||
// If we’ve reached or exceeded the expected length, mark complete
|
||||
if (gUDSContext.UDS_bytesReceived >= gUDSContext.UDS_expectedLength) {
|
||||
gUDSContext.UDS_inProgress = false;
|
||||
// #ifdef DEBUG_LOG
|
||||
// logging.println("Recived all expected UDS bytes");
|
||||
// #endif // DEBUG_LOG
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -153,15 +150,6 @@ uint8_t BmwPhevBattery::increment_alive_counter(uint8_t counter) {
|
|||
return counter;
|
||||
}
|
||||
|
||||
static byte increment_0C0_counter(byte counter) {
|
||||
counter++;
|
||||
// Reset to 0xF0 if it exceeds 0xFE
|
||||
if (counter > 0xFE) {
|
||||
counter = 0xF0;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void BmwPhevBattery::processCellVoltages() {
|
||||
const int startByte = 3; // Start reading at byte 3
|
||||
const int numVoltages = 96; // Number of cell voltage values to process
|
||||
|
@ -189,16 +177,17 @@ void BmwPhevBattery::wake_battery_via_canbus() {
|
|||
// Followed by a Recessive interval of at least ~3 µs (min) and at most ~10 µs (max)
|
||||
// Then a second dominant pulse of similar timing.
|
||||
|
||||
auto original_speed = change_can_speed(CAN_Speed::CAN_SPEED_100KBPS);
|
||||
change_can_speed(CAN_Speed::CAN_SPEED_100KBPS);
|
||||
|
||||
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST);
|
||||
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST);
|
||||
|
||||
change_can_speed(original_speed);
|
||||
// FIXME: This might not wait for the above frames to send before it changes
|
||||
// the speed back. A state-machine controlled delay is likely necessary.
|
||||
|
||||
reset_can_speed();
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sent magic wakeup packet to SME at 100kbps...");
|
||||
#endif
|
||||
}
|
||||
|
||||
void BmwPhevBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
|
@ -246,9 +235,7 @@ void BmwPhevBattery::update_values() { //This function maps all the values fetc
|
|||
datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop
|
||||
datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop
|
||||
set_event(EVENT_STALE_VALUE, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Stale Min/Max voltage values detected during charge/discharge sending - 9999mV...");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive
|
||||
|
@ -398,11 +385,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
rx_frame.data.u8[4] ==
|
||||
0xAD) { //Balancing Status 01 Active 03 Not Active 7DLC F1 05 71 03 AD 6B 01
|
||||
balancing_status = (rx_frame.data.u8[6]);
|
||||
// #ifdef DEBUG_LOG
|
||||
|
||||
// logging.println("Balancing Status received");
|
||||
|
||||
// #endif // DEBUG_LOG
|
||||
//logging.println("Balancing Status received");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -481,7 +464,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
#endif // DEBUG_LOG && UDS_LOG
|
||||
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME);
|
||||
gUDSContext.receivedInBatch = 0; // Reset batch count
|
||||
Serial.println("Sent FC for next batch of 3 frames.");
|
||||
logging.println("Sent FC for next batch of 3 frames.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -525,20 +508,18 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_current = ((int32_t)((gUDSContext.UDS_buffer[3] << 24) | (gUDSContext.UDS_buffer[4] << 16) |
|
||||
(gUDSContext.UDS_buffer[5] << 8) | gUDSContext.UDS_buffer[6])) *
|
||||
0.1;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Received current/amps measurement data: ");
|
||||
logging.printf("Received current/amps measurement data: ");
|
||||
logging.print(battery_current);
|
||||
logging.print(" - ");
|
||||
logging.printf(" - ");
|
||||
for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) {
|
||||
// Optional leading zero for single-digit hex
|
||||
if (gUDSContext.UDS_buffer[i] < 0x10) {
|
||||
logging.print("0");
|
||||
logging.printf("0");
|
||||
}
|
||||
logging.print(gUDSContext.UDS_buffer[i], HEX);
|
||||
logging.print(" ");
|
||||
logging.printf(" ");
|
||||
}
|
||||
logging.println(); // new line at the end
|
||||
#endif // DEBUG_LOG
|
||||
logging.println(""); // new line at the end
|
||||
}
|
||||
|
||||
//Cell Min/Max
|
||||
|
@ -553,19 +534,17 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
min_cell_voltage = (gUDSContext.UDS_buffer[9] << 8 | gUDSContext.UDS_buffer[10]) / 10;
|
||||
max_cell_voltage = (gUDSContext.UDS_buffer[11] << 8 | gUDSContext.UDS_buffer[12]) / 10;
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell Min Max Invalid 65535 or 0...");
|
||||
logging.print("Received data: ");
|
||||
logging.printf("Received data: ");
|
||||
for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) {
|
||||
// Optional leading zero for single-digit hex
|
||||
if (gUDSContext.UDS_buffer[i] < 0x10) {
|
||||
logging.print("0");
|
||||
logging.printf("0");
|
||||
}
|
||||
logging.print(gUDSContext.UDS_buffer[i], HEX);
|
||||
logging.print(" ");
|
||||
logging.printf(" ");
|
||||
}
|
||||
logging.println(); // new line at the end
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,16 +597,12 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
if (rx_frame.data.u8[6] > 0 && rx_frame.data.u8[6] < 255) {
|
||||
battery_temperature_min = (rx_frame.data.u8[6] - 50);
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Pre parsed Cell Temp Min is Invalid ");
|
||||
#endif
|
||||
}
|
||||
if (rx_frame.data.u8[7] > 0 && rx_frame.data.u8[7] < 255) {
|
||||
battery_temperature_max = (rx_frame.data.u8[7] - 50);
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Pre parsed Cell Temp Max is Invalid ");
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -698,7 +673,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
|
||||
void BmwPhevBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, "BMW PHEV Battery", 63);
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
//Wakeup the SME
|
||||
wake_battery_via_canbus();
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "BMW-PHEV-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef BMW_PHEV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS BmwPhevBattery
|
||||
#endif
|
||||
|
||||
class BmwPhevBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -14,6 +10,8 @@ class BmwPhevBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
static constexpr const char* Name = "BMW PHEV Battery";
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/logging.h"
|
||||
|
||||
uint8_t reverse_bits(uint8_t byte) {
|
||||
uint8_t reversed = 0;
|
||||
|
@ -122,9 +123,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
SBOX_100.data.u8[0] = 0x86; // Precharge relay only
|
||||
prechargeStartTime = currentMillis;
|
||||
contactorStatus = NEGATIVE;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Precharge relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Precharge relay engaged");
|
||||
break;
|
||||
case NEGATIVE:
|
||||
if (currentMillis - prechargeStartTime >= CONTACTOR_CONTROL_T1) {
|
||||
|
@ -132,9 +131,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
negativeStartTime = currentMillis;
|
||||
contactorStatus = POSITIVE;
|
||||
datalayer.shunt.precharging = true;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Negative relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Negative relay engaged");
|
||||
}
|
||||
break;
|
||||
case POSITIVE:
|
||||
|
@ -145,18 +142,14 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
positiveStartTime = currentMillis;
|
||||
contactorStatus = PRECHARGE_OFF;
|
||||
datalayer.shunt.precharging = false;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Positive relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Positive relay engaged");
|
||||
}
|
||||
break;
|
||||
case PRECHARGE_OFF:
|
||||
if (currentMillis - positiveStartTime >= CONTACTOR_CONTROL_T3) {
|
||||
SBOX_100.data.u8[0] = 0x6A; // Negative + Positive
|
||||
contactorStatus = COMPLETED;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Precharge relay released");
|
||||
#endif
|
||||
logging.println("S-BOX Precharge relay released");
|
||||
datalayer.shunt.contactors_engaged = true;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "BOLT-AMPERA-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
|
@ -95,14 +96,14 @@ void BoltAmperaBattery::update_values() { //This function maps all the values f
|
|||
} else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) {
|
||||
// When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
MAX_CHARGE_POWER_ALLOWED_W *
|
||||
datalayer.battery.status.override_charge_power_W *
|
||||
(1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
|
||||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W;
|
||||
datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W;
|
||||
}
|
||||
|
||||
// Discharge power is also set in .h file (TODO: Remove this estimation when real value has been found)
|
||||
datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W;
|
||||
datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = temperature_lowest_C * 10;
|
||||
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "BOLT-AMPERA-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef BOLT_AMPERA_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS BoltAmperaBattery
|
||||
#endif
|
||||
|
||||
class BoltAmperaBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -20,8 +16,6 @@ class BoltAmperaBattery : public CanBattery {
|
|||
|
||||
private:
|
||||
BoltAmperaHtmlRenderer renderer;
|
||||
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000;
|
||||
static const int MAX_CHARGE_POWER_ALLOWED_W = 10000;
|
||||
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
|
||||
static const int RAMPDOWN_SOC =
|
||||
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
#include "BYD-ATTO-3-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
/* Notes
|
||||
SOC% by default is now ESTIMATED.
|
||||
If you have a crash-locked pack, See the Wiki for more info on how to attempt an unlock
|
||||
After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the BYD-ATTO-3-BATTERY.h file
|
||||
*/
|
||||
|
||||
#define POLL_FOR_BATTERY_VOLTAGE 0x0008
|
||||
#define POLL_FOR_BATTERY_CURRENT 0x0009
|
||||
#define POLL_FOR_LOWEST_TEMP_CELL 0x002f
|
||||
|
@ -142,6 +137,16 @@ uint16_t estimateSOCstandard(uint16_t packVoltage) { // Linear interpolation fu
|
|||
return 0; // Default return for safety, should never reach here
|
||||
}
|
||||
|
||||
uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte
|
||||
{
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
sum += u8[i];
|
||||
}
|
||||
uint8_t lsb = static_cast<uint8_t>(sum & 0xFF);
|
||||
return static_cast<uint8_t>(~lsb & 0xFF);
|
||||
}
|
||||
|
||||
void BydAttoBattery::
|
||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
|
@ -389,6 +394,7 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_voltage = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[0];
|
||||
//battery_temperature_something = rx_frame.data.u8[7] - 40; resides in frame 7
|
||||
BMS_voltage_available = true;
|
||||
break;
|
||||
case 0x445:
|
||||
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
@ -538,10 +544,17 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
|
||||
if (counter_100ms > 3) {
|
||||
ATTO_3_441.data.u8[4] = 0x9D;
|
||||
ATTO_3_441.data.u8[5] = 0x01;
|
||||
ATTO_3_441.data.u8[6] = 0xFF;
|
||||
ATTO_3_441.data.u8[7] = 0xF5;
|
||||
if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors
|
||||
ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1);
|
||||
ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8);
|
||||
ATTO_3_441.data.u8[6] = 0xFF;
|
||||
ATTO_3_441.data.u8[7] = compute441Checksum(ATTO_3_441.data.u8);
|
||||
} else {
|
||||
ATTO_3_441.data.u8[4] = 0x0C;
|
||||
ATTO_3_441.data.u8[5] = 0x00;
|
||||
ATTO_3_441.data.u8[6] = 0xFF;
|
||||
ATTO_3_441.data.u8[7] = 0x87;
|
||||
}
|
||||
}
|
||||
|
||||
transmit_can_frame(&ATTO_3_441);
|
||||
|
|
|
@ -7,8 +7,12 @@
|
|||
#include "BYD-ATTO-3-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \
|
||||
// Comment out this only if you know your BMS is unlocked and able to send SOC%
|
||||
/* Notes
|
||||
SOC% by default is now MEASURED by BMS.
|
||||
If you have a crash-locked pack, See the Wiki for more info on how to attempt an unlock.
|
||||
Remove the comment from "USE_ESTIMATED_SOC" below if you still decide to use a locked battery and want to use estimated SOC.
|
||||
*/
|
||||
//#define USE_ESTIMATED_SOC
|
||||
|
||||
//Uncomment and configure this line, if you want to filter out a broken temperature sensor (1-10)
|
||||
//Make sure you understand risks associated with disabling. Values can be read via "More Battery info"
|
||||
|
@ -20,9 +24,6 @@ static const int RAMPDOWN_POWER_ALLOWED =
|
|||
10000; // Power to start ramp down from, set a lower value to limit the power even further as SOC decreases
|
||||
|
||||
/* Do not modify the rows below */
|
||||
#ifdef BYD_ATTO_3_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS BydAttoBattery
|
||||
#endif
|
||||
|
||||
class BydAttoBattery : public CanBattery {
|
||||
public:
|
||||
|
@ -94,6 +95,7 @@ class BydAttoBattery : public CanBattery {
|
|||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
|
||||
bool SOC_method = false;
|
||||
bool BMS_voltage_available = false;
|
||||
uint8_t counter_50ms = 0;
|
||||
uint8_t counter_100ms = 0;
|
||||
uint8_t frame6_counter = 0xB;
|
||||
|
|
|
@ -45,6 +45,9 @@ enum class BatteryType {
|
|||
MgHsPhev = 37,
|
||||
SamsungSdiLv = 38,
|
||||
HyundaiIoniq28 = 39,
|
||||
Kia64FD = 40,
|
||||
RelionBattery = 41,
|
||||
RivianBattery = 42,
|
||||
Highest
|
||||
};
|
||||
|
||||
|
@ -73,6 +76,7 @@ class Battery {
|
|||
|
||||
virtual bool supports_clear_isolation() { return false; }
|
||||
virtual bool supports_reset_BMS() { return false; }
|
||||
virtual bool supports_reset_SOC() { return false; }
|
||||
virtual bool supports_reset_crash() { return false; }
|
||||
virtual bool supports_reset_NVROL() { return false; }
|
||||
virtual bool supports_reset_DTC() { return false; }
|
||||
|
@ -91,6 +95,7 @@ class Battery {
|
|||
|
||||
virtual void clear_isolation() {}
|
||||
virtual void reset_BMS() {}
|
||||
virtual void reset_SOC() {}
|
||||
virtual void reset_crash() {}
|
||||
virtual void reset_contactor() {}
|
||||
virtual void reset_NVROL() {}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "CELLPOWER-BMS.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
|
||||
|
@ -19,9 +20,11 @@ void CellPowerBms::update_values() {
|
|||
|
||||
datalayer.battery.status.current_dA = battery_pack_current_dA;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = 5000; //TODO, is this available via CAN?
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
datalayer.battery.status.override_charge_power_W; //TODO, is this available via CAN?
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN?
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
datalayer.battery.status.override_discharge_power_W; //TODO, is this available via CAN?
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = (int16_t)(pack_temperature_low_C * 10);
|
||||
|
||||
|
@ -231,8 +234,8 @@ void CellPowerBms::setup(void) { // Performs one time setup at startup
|
|||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "CELLPOWER-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef CELLPOWER_BMS
|
||||
#define SELECTED_BATTERY_CLASS CellPowerBms
|
||||
#endif
|
||||
|
||||
class CellPowerBms : public CanBattery {
|
||||
public:
|
||||
CellPowerBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
|
||||
|
@ -22,11 +18,6 @@ class CellPowerBms : public CanBattery {
|
|||
|
||||
private:
|
||||
CellpowerHtmlRenderer renderer;
|
||||
/* Tweak these according to your battery build */
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent
|
||||
|
||||
|
|
|
@ -116,15 +116,13 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
x102_chg_session.ChargingCurrentRequest = newChargingCurrentRequest;
|
||||
x102_chg_session.TargetBatteryVoltage = newTargetBatteryVoltage;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
//Note on p131
|
||||
uint8_t chargingrate = 0;
|
||||
if (x100_chg_lim.ConstantOfChargingRateIndication > 0) {
|
||||
chargingrate = x102_chg_session.StateOfCharge / x100_chg_lim.ConstantOfChargingRateIndication * 100;
|
||||
logging.print("Charge Rate (kW): ");
|
||||
logging.printf("Charge Rate (kW): ");
|
||||
logging.println(chargingrate);
|
||||
}
|
||||
#endif
|
||||
|
||||
//Table A.26—Charge control termination command patterns -- should echo x108 handling
|
||||
|
||||
|
@ -136,41 +134,31 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
*/
|
||||
if ((CHADEMO_Status == CHADEMO_INIT && vehicle_permission) ||
|
||||
(x102_chg_session.s.status.StatusVehicleChargingEnabled && !vehicle_permission)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inconsistent charge/discharge state.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryOverVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery over voltage.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryUnderVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery under voltage.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryCurrentDeviation) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery current deviation. Possible EVSE issue?");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryVoltageDeviation) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery voltage deviation. Possible EVSE issue?");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
@ -183,18 +171,14 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
|
||||
//FIXME condition nesting or more stanzas needed here for clear determination of cessation reason
|
||||
if (CHADEMO_Status == CHADEMO_POWERFLOW && EVSE_mode == CHADEMO_CHARGE && !vehicle_permission) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("State of charge ceiling reached or charging interrupted, stop charging");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle_permission && CHADEMO_Status == CHADEMO_NEGOTIATE) {
|
||||
CHADEMO_Status = CHADEMO_EV_ALLOWED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("STATE shift to CHADEMO_EV_ALLOWED in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -203,23 +187,17 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
// consider relocating
|
||||
if (vehicle_permission && CHADEMO_Status == CHADEMO_EVSE_PREPARE && priorTargetBatteryVoltage == 0 &&
|
||||
newTargetBatteryVoltage > 0 && x102_chg_session.s.status.StatusVehicleChargingEnabled) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("STATE SHIFT to EVSE_START reached in process_vehicle_charging_session()");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_EVSE_START;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle_permission && evse_permission && CHADEMO_Status == CHADEMO_POWERFLOW) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("updating vehicle request in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED CHADEMO STATE, try unplugging chademo cable, reboot emulator, and retry!");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -231,8 +209,7 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
|
|||
x200_discharge_limits.MinimumBatteryDischargeLevel = rx_frame.data.u8[6];
|
||||
x200_discharge_limits.MaxRemainingCapacityForCharging = rx_frame.data.u8[7];
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
/* unsigned long currentMillis = millis();
|
||||
/* unsigned long currentMillis = millis();
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
logging.println("x200 Max remaining capacity for charging/discharging:");
|
||||
|
@ -240,16 +217,13 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
|
|||
logging.println(0xFF - x200_discharge_limits.MaxRemainingCapacityForCharging);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
logging.print("Measured: ");
|
||||
logging.printf("Measured: ");
|
||||
logging.print(get_measured_voltage());
|
||||
logging.print("Minimum voltage: ");
|
||||
logging.printf("Minimum voltage: ");
|
||||
logging.print(x200_discharge_limits.MinimumDischargeVoltage);
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
}
|
||||
|
@ -264,15 +238,13 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) {
|
|||
x201_discharge_estimate.ApproxDischargeCompletionTime = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]);
|
||||
x201_discharge_estimate.AvailableVehicleEnergy = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
logging.print("x201 availabile vehicle energy, completion time: ");
|
||||
logging.printf("x201 availabile vehicle energy, completion time: ");
|
||||
logging.println(x201_discharge_estimate.AvailableVehicleEnergy);
|
||||
logging.print("x201 approx vehicle completion time: ");
|
||||
logging.printf("x201 approx vehicle completion time: ");
|
||||
logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ChademoBattery::process_vehicle_dynamic_control(CAN_frame rx_frame) {
|
||||
|
@ -615,10 +587,8 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
|
|||
// TODO need an update_evse_dynamic_control(..) function above before we send 118
|
||||
// 110.0.0
|
||||
if (x102_chg_session.ControlProtocolNumberEV >= 0x03) { //Only send the following on Chademo 2.0 vehicles?
|
||||
#ifdef DEBUG_LOG
|
||||
//FIXME REMOVE
|
||||
logging.println("REMOVE: proto 2.0");
|
||||
#endif
|
||||
transmit_can_frame(&CHADEMO_118);
|
||||
}
|
||||
}
|
||||
|
@ -656,16 +626,12 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* ------------------- State override conditions checks ------------------- */
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && x102_chg_session.s.status.StatusVehicleShifterPosition) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle is not parked, abort.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && !vehicle_permission) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle charge/discharge permission ended, stop.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
|
@ -678,25 +644,19 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
plug_inserted = digitalRead(pin7);
|
||||
|
||||
if (!plug_inserted) {
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO plug is not inserted.");
|
||||
#endif
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO plug is not inserted.");
|
||||
return;
|
||||
}
|
||||
|
||||
CHADEMO_Status = CHADEMO_CONNECTED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO plug is inserted. Provide EVSE power to vehicle to trigger initialization.");
|
||||
#endif
|
||||
|
||||
break;
|
||||
case CHADEMO_CONNECTED:
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
//logging.println("CHADEMO_CONNECTED State");
|
||||
#endif
|
||||
/* plug_inserted is .. essentially a volatile of sorts, so verify */
|
||||
if (plug_inserted) {
|
||||
/* If connection is detectable, jumpstart handshake by
|
||||
|
@ -729,17 +689,13 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* Vehicle and EVSE dance */
|
||||
//TODO if pin 4 / j goes high,
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO_NEGOTIATE State");
|
||||
#endif
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO_NEGOTIATE State");
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
break;
|
||||
case CHADEMO_EV_ALLOWED:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EV_ALLOWED State");
|
||||
#endif
|
||||
// If we are in this state, vehicle_permission was already set to true...but re-verify
|
||||
// that pin 4 (j) reads high
|
||||
if (vehicle_permission) {
|
||||
|
@ -754,10 +710,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
}
|
||||
break;
|
||||
case CHADEMO_EVSE_PREPARE:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_PREPARE State");
|
||||
#endif
|
||||
/* TODO voltage check of output < 20v
|
||||
* insulation test hypothetically happens here before triggering PIN 10 high
|
||||
* see Table A.28—Requirements for the insulation test for output DC circuit
|
||||
|
@ -790,19 +744,15 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
//state changes to CHADEMO_EVSE_START only upon receipt of charging session request
|
||||
break;
|
||||
case CHADEMO_EVSE_START:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_START State");
|
||||
#endif
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
x109_evse_state.s.status.EVSE_status = 0;
|
||||
|
||||
CHADEMO_Status = CHADEMO_EVSE_CONTACTORS_ENABLED;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Initiating contactors");
|
||||
#endif
|
||||
|
||||
/* break rather than fall through because contactors are not instantaneous;
|
||||
* worth giving it a cycle to finish
|
||||
|
@ -810,18 +760,14 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_EVSE_CONTACTORS_ENABLED:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_CONTACTORS State");
|
||||
#endif
|
||||
|
||||
/* check whether contactors ready, because externally dependent upon inverter allow during discharge */
|
||||
if (contactors_ready) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Contactors ready");
|
||||
logging.print("Voltage: ");
|
||||
logging.printf("Voltage: ");
|
||||
logging.println(get_measured_voltage());
|
||||
#endif
|
||||
/* transition to POWERFLOW state if discharge compatible on both sides */
|
||||
if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible &&
|
||||
(EVSE_mode == CHADEMO_DISCHARGE || EVSE_mode == CHADEMO_BIDIRECTIONAL)) {
|
||||
|
@ -840,10 +786,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* break or fall through ? TODO */
|
||||
break;
|
||||
case CHADEMO_POWERFLOW:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_POWERFLOW State");
|
||||
#endif
|
||||
/* POWERFLOW for charging, discharging, and bidirectional */
|
||||
/* Interpretation */
|
||||
if (x102_chg_session.s.status.StatusVehicleShifterPosition) {
|
||||
|
@ -860,9 +804,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
}
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
|
@ -871,10 +813,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
x109_evse_state.s.status.EVSE_status = 1;
|
||||
break;
|
||||
case CHADEMO_STOP:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_STOP State");
|
||||
#endif
|
||||
/* back to CHADEMO_IDLE after teardown */
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
x109_evse_state.s.status.EVSE_status = 0;
|
||||
|
@ -899,17 +839,13 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_FAULT:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_FAULT State");
|
||||
#endif
|
||||
/* Once faulted, never departs CHADEMO_FAULT state unless device is power cycled as a safety measure */
|
||||
x109_evse_state.s.status.EVSE_error = 1;
|
||||
x109_evse_state.s.status.ChgDischError = 1;
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO fault encountered, tearing down to make safe");
|
||||
#endif
|
||||
digitalWrite(pin10, LOW);
|
||||
digitalWrite(pin2, LOW);
|
||||
evse_permission = false;
|
||||
|
@ -919,9 +855,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED CHADEMO_STATE, setting FAULT");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -7,13 +7,6 @@
|
|||
#include "CHADEMO-BATTERY-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS ChademoBattery
|
||||
|
||||
//Contactor control is required for CHADEMO support
|
||||
#define CONTACTOR_CONTROL
|
||||
#endif
|
||||
|
||||
class ChademoBattery : public CanBattery {
|
||||
public:
|
||||
ChademoBattery() {
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "CHADEMO-BATTERY.h"
|
||||
#include "src/devboard/utils/logging.h"
|
||||
|
||||
/* Initial frames received from ISA shunts provide invalid during initialization */
|
||||
static int framecount = 0;
|
||||
|
@ -87,17 +86,6 @@ void ISA_handleFrame(CAN_frame* frame) {
|
|||
|
||||
case 0x510:
|
||||
case 0x511:
|
||||
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
logging.print(" ");
|
||||
logging.print(frame->ID, HEX);
|
||||
logging.print(" ");
|
||||
logging.print(frame->DLC);
|
||||
logging.print(" ");
|
||||
for (int i = 0; i < frame->DLC; ++i) {
|
||||
logging.print(frame->data.u8[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
logging.println("");
|
||||
break;
|
||||
|
||||
case 0x521:
|
||||
|
@ -245,7 +233,7 @@ void ISA_initialize() {
|
|||
ISA_STOP();
|
||||
delay(500);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
logging.print("ISA Initialization ");
|
||||
logging.printf("ISA Initialization ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x20 + i);
|
||||
|
@ -382,7 +370,7 @@ void ISA_initCurrent() {
|
|||
}
|
||||
|
||||
void ISA_getCONFIG(uint8_t i) {
|
||||
logging.print("ISA Get Config ");
|
||||
logging.printf("ISA Get Config ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x60 + i);
|
||||
|
@ -398,7 +386,7 @@ void ISA_getCONFIG(uint8_t i) {
|
|||
}
|
||||
|
||||
void ISA_getCAN_ID(uint8_t i) {
|
||||
logging.print("ISA Get CAN ID ");
|
||||
logging.printf("ISA Get CAN ID ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x50 + i);
|
||||
|
@ -418,8 +406,8 @@ void ISA_getCAN_ID(uint8_t i) {
|
|||
}
|
||||
|
||||
void ISA_getINFO(uint8_t i) {
|
||||
logging.print("ISA Get INFO ");
|
||||
logging.println(i, HEX);
|
||||
logging.printf("ISA Get INFO ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x70 + i);
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "CMFA-EV-BATTERY.h"
|
||||
#include <cstring> //unit tests memcpy
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "CMFA-EV-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef CMFA_EV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS CmfaEvBattery
|
||||
#endif
|
||||
|
||||
class CmfaEvBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
#include "CanBattery.h"
|
||||
|
||||
CanBattery::CanBattery(CAN_Speed speed) {
|
||||
can_interface = can_config.battery;
|
||||
CanBattery::CanBattery(CAN_Speed speed) : CanBattery(can_config.battery, speed) {}
|
||||
|
||||
CanBattery::CanBattery(CAN_Interface interface, CAN_Speed speed) {
|
||||
can_interface = interface;
|
||||
initial_speed = speed;
|
||||
register_transmitter(this);
|
||||
register_can_receiver(this, can_interface, speed);
|
||||
}
|
||||
|
||||
CAN_Speed CanBattery::change_can_speed(CAN_Speed speed) {
|
||||
bool CanBattery::change_can_speed(CAN_Speed speed) {
|
||||
return ::change_can_speed(can_interface, speed);
|
||||
}
|
||||
|
||||
void CanBattery::reset_can_speed() {
|
||||
::change_can_speed(can_interface, initial_speed);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "Battery.h"
|
||||
|
||||
#include "../../USER_SETTINGS.h"
|
||||
#include "../../src/communication/Transmitter.h"
|
||||
#include "../../src/communication/can/CanReceiver.h"
|
||||
#include "../../src/communication/can/comm_can.h"
|
||||
|
@ -23,16 +22,13 @@ class CanBattery : public Battery, Transmitter, CanReceiver {
|
|||
|
||||
protected:
|
||||
CAN_Interface can_interface;
|
||||
CAN_Speed initial_speed;
|
||||
|
||||
CanBattery(CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS);
|
||||
CanBattery(CAN_Interface interface, CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS);
|
||||
|
||||
CanBattery(CAN_Interface interface, CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS) {
|
||||
can_interface = interface;
|
||||
register_transmitter(this);
|
||||
register_can_receiver(this, can_interface, speed);
|
||||
}
|
||||
|
||||
CAN_Speed change_can_speed(CAN_Speed speed);
|
||||
bool change_can_speed(CAN_Speed speed);
|
||||
void reset_can_speed();
|
||||
|
||||
void transmit_can_frame(const CAN_frame* frame) { transmit_can_frame_to_interface(frame, can_interface); }
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "DALY-BMS.h"
|
||||
#include <Arduino.h>
|
||||
#include <cstdint>
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/hal/hal.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -14,8 +15,9 @@ static int16_t current_dA = 0;
|
|||
static uint16_t voltage_dV = 0;
|
||||
static uint32_t remaining_capacity_mAh = 0;
|
||||
static uint16_t cellvoltages_mV[48] = {0};
|
||||
static uint16_t cellvoltage_min_mV = 0;
|
||||
static uint16_t cellvoltage_min_mV = 3700;
|
||||
static uint16_t cellvoltage_max_mV = 0;
|
||||
static uint16_t cell_count = 0;
|
||||
static uint16_t SOC = 0;
|
||||
static bool has_fault = false;
|
||||
|
||||
|
@ -25,8 +27,9 @@ void DalyBms::update_values() {
|
|||
datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0)
|
||||
datalayer.battery.status.remaining_capacity_Wh = (remaining_capacity_mAh * (uint32_t)voltage_dV) / 10000;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (BATTERY_MAX_CHARGE_AMP * voltage_dV) / 100;
|
||||
datalayer.battery.status.max_discharge_power_W = (BATTERY_MAX_DISCHARGE_AMP * voltage_dV) / 100;
|
||||
datalayer.battery.status.max_charge_power_W = (datalayer.battery.settings.max_user_set_charge_dA * voltage_dV) / 100;
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
(datalayer.battery.settings.max_user_set_discharge_dA * voltage_dV) / 100;
|
||||
|
||||
// limit power when SoC is low or high
|
||||
uint32_t adaptive_power_limit = 999999;
|
||||
|
@ -54,6 +57,9 @@ void DalyBms::update_values() {
|
|||
datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV;
|
||||
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV;
|
||||
|
||||
// Use the received value from the BMS, to avoid needing to configure it
|
||||
datalayer.battery.info.number_of_cells = cell_count;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = temperature_min_dC;
|
||||
datalayer.battery.status.temperature_max_dC = temperature_max_dC;
|
||||
|
||||
|
@ -63,12 +69,10 @@ void DalyBms::update_values() {
|
|||
void DalyBms::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.number_of_cells = CELL_COUNT;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
|
||||
datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
|
||||
auto rx_pin = esp32hal->RS485_RX_PIN();
|
||||
|
@ -104,18 +108,16 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) {
|
|||
((uint32_t)data[offset + 3]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
void dump_buff(const char* msg, uint8_t* buff, uint8_t len) {
|
||||
Serial.print("[DALY-BMS] ");
|
||||
Serial.print(msg);
|
||||
logging.printf("[DALY-BMS] ");
|
||||
logging.printf(msg);
|
||||
for (int i = 0; i < len; i++) {
|
||||
Serial.print(buff[i] >> 4, HEX);
|
||||
Serial.print(buff[i] & 0xf, HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(buff[i] >> 4, HEX);
|
||||
logging.print(buff[i] & 0xf, HEX);
|
||||
logging.printf(" ");
|
||||
}
|
||||
Serial.println();
|
||||
logging.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
void decode_packet(uint8_t command, uint8_t data[8]) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
@ -138,6 +140,7 @@ void decode_packet(uint8_t command, uint8_t data[8]) {
|
|||
remaining_capacity_mAh = decode_uint32be(data, 4);
|
||||
break;
|
||||
case 0x94:
|
||||
cell_count = data[0];
|
||||
break;
|
||||
case 0x95:
|
||||
if (data[0] > 0 && data[0] <= 16) {
|
||||
|
@ -175,9 +178,7 @@ void DalyBms::transmit_rs485(unsigned long currentMillis) {
|
|||
tx_buff[2] = nextCommand;
|
||||
tx_buff[3] = 8;
|
||||
tx_buff[12] = calculate_checksum(tx_buff);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("transmitting: ", tx_buff, 13);
|
||||
#endif
|
||||
Serial2.write(tx_buff, 13);
|
||||
nextCommand++;
|
||||
if (nextCommand > 0x98)
|
||||
|
@ -197,17 +198,12 @@ void DalyBms::receive() {
|
|||
if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 ||
|
||||
recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 ||
|
||||
recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) {
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("dropping partial rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
recv_len = 0;
|
||||
}
|
||||
|
||||
if (recv_len > 12) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("decoding successfull rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
decode_packet(recv_buff[2], &recv_buff[4]);
|
||||
recv_len = 0;
|
||||
lastPacket = millis();
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "RS485Battery.h"
|
||||
|
||||
#ifdef DALY_BMS
|
||||
#define SELECTED_BATTERY_CLASS DalyBms
|
||||
#endif
|
||||
|
||||
class DalyBms : public RS485Battery {
|
||||
public:
|
||||
void setup();
|
||||
|
@ -17,11 +13,6 @@ class DalyBms : public RS485Battery {
|
|||
|
||||
private:
|
||||
/* Tweak these according to your battery build */
|
||||
static const int CELL_COUNT = 14;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 580; //580 = 58.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 460; //480 = 48.0V
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 3200; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int POWER_PER_PERCENT =
|
||||
50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...)
|
||||
static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C
|
||||
|
|
|
@ -5,51 +5,118 @@
|
|||
#include "../devboard/utils/events.h"
|
||||
|
||||
/* TODO:
|
||||
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
|
||||
- Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
|
||||
This integration is still ongoing. The same integration can be used on multiple variants of the Stellantis platforms
|
||||
- eCMP: Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
|
||||
|
||||
- MysteryVan: Map more values from constantly transmitted instead of PID
|
||||
- ADD CAN sending towards the battery (CAN-logs of full vehicle wanted!)
|
||||
- Following CAN messages need to be sent towards it:
|
||||
- VCU: 4C9 , 565 , 398, 448, 458, 4F1 , 342, 3E2 , 402 , 422 , 482 4D1
|
||||
- CMM: 478 , 558, 1A8, 4B8 1F8 498 4E8
|
||||
- OBC: 531 441 541 551 3C1
|
||||
- BSIInfo_382
|
||||
- VCU_BSI_Wakeup_27A
|
||||
- V2_BSI_552
|
||||
- CRASH_4C8
|
||||
- EVSE plug in (optional): 108, 109, 119
|
||||
- CRASH_4C8
|
||||
- CRNT_SENS_095
|
||||
- MCU 526
|
||||
- JDD 55F NEW
|
||||
|
||||
- STLA medium: Everything missing
|
||||
- ADD CAN sending towards the battery (CAN-logs of full vehicle wanted!)
|
||||
*/
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
void EcmpBattery::update_values() {
|
||||
|
||||
datalayer.battery.status.real_soc = battery_soc * 10;
|
||||
if (!MysteryVan) { //Normal eCMP platform
|
||||
datalayer.battery.status.real_soc = battery_soc * 10;
|
||||
|
||||
datalayer.battery.status.soh_pptt;
|
||||
datalayer.battery.status.soh_pptt;
|
||||
|
||||
datalayer.battery.status.voltage_dV = battery_voltage * 10;
|
||||
datalayer.battery.status.voltage_dV = battery_voltage * 10;
|
||||
|
||||
datalayer.battery.status.current_dA = -(battery_current * 10);
|
||||
datalayer.battery.status.current_dA = -(battery_current * 10);
|
||||
|
||||
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage;
|
||||
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage;
|
||||
datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10;
|
||||
datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage;
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10;
|
||||
datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10;
|
||||
|
||||
// Initialize min and max, lets find which cells are min and max!
|
||||
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t max_cell_mv_value = 0;
|
||||
// Loop to find the min and max while ignoring zero values
|
||||
for (uint8_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) {
|
||||
uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i];
|
||||
if (voltage_mV != 0) { // Skip unread values (0)
|
||||
min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV);
|
||||
max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV);
|
||||
datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10;
|
||||
|
||||
// Initialize min and max, lets find which cells are min and max!
|
||||
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t max_cell_mv_value = 0;
|
||||
// Loop to find the min and max while ignoring zero values
|
||||
for (uint8_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) {
|
||||
uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i];
|
||||
if (voltage_mV != 0) { // Skip unread values (0)
|
||||
min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV);
|
||||
max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV);
|
||||
}
|
||||
}
|
||||
// If all array values are 0, reset min/max to 3700
|
||||
if (min_cell_mv_value == std::numeric_limits<uint16_t>::max()) {
|
||||
min_cell_mv_value = 3700;
|
||||
max_cell_mv_value = 3700;
|
||||
}
|
||||
}
|
||||
// If all array values are 0, reset min/max to 3700
|
||||
if (min_cell_mv_value == std::numeric_limits<uint16_t>::max()) {
|
||||
min_cell_mv_value = 3700;
|
||||
max_cell_mv_value = 3700;
|
||||
}
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
|
||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
|
||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
|
||||
} else { //Some variant of the 50/75kWh battery that is not using the eCMP CAN mappings.
|
||||
// For these batteries we need to use the OBD2 PID polled values
|
||||
|
||||
if (pid_energy_capacity != NOT_SAMPLED_YET) {
|
||||
datalayer.battery.status.remaining_capacity_Wh = pid_energy_capacity;
|
||||
// calculate SOC based on datalayer.battery.info.total_capacity_Wh and remaining_capacity_Wh
|
||||
datalayer.battery.status.real_soc = (uint16_t)(((float)datalayer.battery.status.remaining_capacity_Wh /
|
||||
datalayer.battery.info.total_capacity_Wh) *
|
||||
10000);
|
||||
}
|
||||
|
||||
datalayer.battery.status.soh_pptt;
|
||||
|
||||
if (pid_pack_voltage != NOT_SAMPLED_YET) {
|
||||
datalayer.battery.status.voltage_dV = pid_pack_voltage + 800;
|
||||
}
|
||||
|
||||
if (pid_current != NOT_SAMPLED_YET) {
|
||||
datalayer.battery.status.current_dA = (int16_t)(pid_current / 100);
|
||||
|
||||
datalayer.battery.status.active_power_W =
|
||||
(uint16_t)((pid_current / 1000.0f) * (datalayer.battery.status.voltage_dV / 10.0f));
|
||||
}
|
||||
|
||||
if (pid_max_charge_10s != NOT_SAMPLED_YET) {
|
||||
datalayer.battery.status.max_charge_power_W = pid_max_charge_10s;
|
||||
}
|
||||
|
||||
if (pid_max_discharge_10s != NOT_SAMPLED_YET) {
|
||||
datalayer.battery.status.max_discharge_power_W = pid_max_discharge_10s;
|
||||
}
|
||||
|
||||
if ((pid_highest_temperature != NOT_SAMPLED_YET) && (pid_lowest_temperature != NOT_SAMPLED_YET)) {
|
||||
datalayer.battery.status.temperature_max_dC = pid_highest_temperature * 10;
|
||||
datalayer.battery.status.temperature_min_dC = pid_lowest_temperature * 10;
|
||||
}
|
||||
|
||||
if ((pid_high_cell_voltage != NOT_SAMPLED_YET) && (pid_low_cell_voltage != NOT_SAMPLED_YET)) {
|
||||
datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage;
|
||||
datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage;
|
||||
}
|
||||
|
||||
datalayer.battery.info.number_of_cells = NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY; //50/75kWh sends valid cellcount
|
||||
}
|
||||
|
||||
// Update extended datalayer (More Battery Info page)
|
||||
datalayer_extended.stellantisECMP.MainConnectorState = battery_MainConnectorState;
|
||||
|
@ -124,6 +191,28 @@ void EcmpBattery::update_values() {
|
|||
datalayer_extended.stellantisECMP.pid_contactor_closing_counter = pid_contactor_closing_counter;
|
||||
datalayer_extended.stellantisECMP.pid_date_of_manufacture = pid_date_of_manufacture;
|
||||
datalayer_extended.stellantisECMP.pid_SOH_cell_1 = pid_SOH_cell_1;
|
||||
// Update extended datalayer for MysteryVan
|
||||
datalayer_extended.stellantisECMP.MysteryVan = MysteryVan;
|
||||
datalayer_extended.stellantisECMP.CONTACTORS_STATE = CONTACTORS_STATE;
|
||||
datalayer_extended.stellantisECMP.CrashMemorized = HV_BATT_CRASH_MEMORIZED;
|
||||
datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON = CONTACTOR_OPENING_REASON;
|
||||
datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE = TBMU_FAULT_TYPE;
|
||||
datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES = HV_BATT_FC_INSU_MINUS_RES;
|
||||
datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES = HV_BATT_FC_INSU_PLUS_RES;
|
||||
datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES = HV_BATT_FC_VHL_INSU_PLUS_RES;
|
||||
datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES;
|
||||
datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES;
|
||||
datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST = ALERT_CELL_POOR_CONSIST;
|
||||
datalayer_extended.stellantisECMP.ALERT_OVERCHARGE = ALERT_OVERCHARGE;
|
||||
datalayer_extended.stellantisECMP.ALERT_BATT = ALERT_BATT;
|
||||
datalayer_extended.stellantisECMP.ALERT_LOW_SOC = ALERT_LOW_SOC;
|
||||
datalayer_extended.stellantisECMP.ALERT_HIGH_SOC = ALERT_HIGH_SOC;
|
||||
datalayer_extended.stellantisECMP.ALERT_SOC_JUMP = ALERT_SOC_JUMP;
|
||||
datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF = ALERT_TEMP_DIFF;
|
||||
datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP = ALERT_HIGH_TEMP;
|
||||
datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE = ALERT_OVERVOLTAGE;
|
||||
datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE = ALERT_CELL_OVERVOLTAGE;
|
||||
datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE = ALERT_CELL_UNDERVOLTAGE;
|
||||
|
||||
if (battery_InterlockOpen) {
|
||||
set_event(EVENT_HVIL_FAILURE, 0);
|
||||
|
@ -145,9 +234,223 @@ void EcmpBattery::update_values() {
|
|||
}
|
||||
|
||||
void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x125: //Common
|
||||
case 0x2D4: //MysteryVan 50/75kWh platform (TBMU 100ms periodic)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
MysteryVan = true;
|
||||
SOE_MAX_CURRENT_TEMP = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; // (Wh, 0-200000)
|
||||
FRONT_MACHINE_POWER_LIMIT = (rx_frame.data.u8[4] << 6) | ((rx_frame.data.u8[5] & 0xFC) >> 2); // (W 0-1000000)
|
||||
REAR_MACHINE_POWER_LIMIT = ((rx_frame.data.u8[5] & 0x03) << 12) | (rx_frame.data.u8[6] << 4) |
|
||||
((rx_frame.data.u8[7] & 0xF0) >> 4); // (W 0-1000000)
|
||||
break;
|
||||
case 0x3B4: //MysteryVan 50/75kWh platform (TBMU 100ms periodic)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
EVSE_INSTANT_DC_HV_CURRENT =
|
||||
((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 2) | ((rx_frame.data.u8[4] & 0xC0) >> 6);
|
||||
EVSE_STATE = ((rx_frame.data.u8[4] & 0x38) >> 3); /*Enumeration below
|
||||
000: NOT CONNECTED
|
||||
001: CONNECTED
|
||||
010: INITIALISATION
|
||||
011: READY
|
||||
100: PRECHARGE IN PROGRESS
|
||||
101: TRANSFER IN PROGRESS
|
||||
110: NOT READY
|
||||
111: Reserved */
|
||||
HV_BATT_SOE_HD = ((rx_frame.data.u8[4] & 0x03) << 12) | (rx_frame.data.u8[5] << 4) |
|
||||
((rx_frame.data.u8[6] & 0xF0) >> 4); // (Wh, 0-200000)
|
||||
HV_BATT_SOE_MAX = ((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[7]; // (Wh, 0-200000)
|
||||
CHECKSUM_FRAME_3B4 = (rx_frame.data.u8[0] & 0xF0) >> 4;
|
||||
COUNTER_3B4 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x2F4: //MysteryVan 50/75kWh platform (Event triggered when charging)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//TBMU_EVSE_DC_MES_VOLTAGE = (rx_frame.data.u8[0] << 6) | (rx_frame.data.u8[1] >> 2); //V 0-1000 //Fastcharger info, not needed for BE
|
||||
//TBMU_EVSE_DC_MIN_VOLTAGE = ((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[2]; //V 0-1000 //Fastcharger info, not needed for BE
|
||||
//TBMU_EVSE_DC_MES_CURRENT = (rx_frame.data.u8[3] << 4) | ((rx_frame.data.u8[4] & 0xF0) >> 4); //A -2000 - 2000 //Fastcharger info, not needed for BE
|
||||
//TBMU_EVSE_CHRG_REQ = (rx_frame.data.u8[4] & 0x0C) >> 2; //00 No request, 01 Stop request //Fastcharger info, not needed for BE
|
||||
//HV_STORAGE_MAX_I = ((rx_frame.data.u8[4] & 0x03) << 12) | (rx_frame.data.u8[5] << 2) | //Fastcharger info, not needed for BE
|
||||
//((rx_frame.data.u8[6] & 0xC0) >> 6); //A -2000 - 2000
|
||||
//TBMU_EVSE_DC_MAX_POWER = ((rx_frame.data.u8[6] & 0x3F) << 8) | rx_frame.data.u8[7]; //W -1000000 - 0 //Fastcharger info, not needed for BE
|
||||
break;
|
||||
case 0x3F4: //MysteryVan 50/75kWh platform (Temperature sensors)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (((rx_frame.data.u8[0] & 0xE0) >> 5)) //Mux resides in top 3 bits of frame0
|
||||
{
|
||||
case 0:
|
||||
BMS_PROBETEMP[0] = (rx_frame.data.u8[1] - 40);
|
||||
BMS_PROBETEMP[1] = (rx_frame.data.u8[2] - 40);
|
||||
BMS_PROBETEMP[2] = (rx_frame.data.u8[3] - 40);
|
||||
BMS_PROBETEMP[3] = (rx_frame.data.u8[4] - 40);
|
||||
BMS_PROBETEMP[4] = (rx_frame.data.u8[5] - 40);
|
||||
BMS_PROBETEMP[5] = (rx_frame.data.u8[6] - 40);
|
||||
BMS_PROBETEMP[6] = (rx_frame.data.u8[7] - 40);
|
||||
break;
|
||||
default: //There are in total 64 temperature measurements in the BMS. We do not need to sample them all.
|
||||
break; //For future, we could read them all if we want to.
|
||||
}
|
||||
break;
|
||||
case 0x554: //MysteryVan 50/75kWh platform (Discharge/Charge limits)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_PEAK_DISCH_POWER_HD = (rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[2] >> 2); //0-1000000 W
|
||||
HV_BATT_PEAK_CH_POWER_HD = ((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 4) |
|
||||
((rx_frame.data.u8[4] & 0xF0) >> 4); // -1000000 - 0 W
|
||||
HV_BATT_NOM_CH_POWER_HD = ((rx_frame.data.u8[4] & 0x0F) << 12) | (rx_frame.data.u8[5] << 6) |
|
||||
((rx_frame.data.u8[6] & 0xC0) >> 6); // -1000000 - 0 W
|
||||
MAX_ALLOW_CHRG_CURRENT = ((rx_frame.data.u8[6] & 0x3F) << 8) | rx_frame.data.u8[7];
|
||||
CHECKSUM_FRAME_554 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xE
|
||||
COUNTER_554 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x373: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
REQ_CLEAR_DTC_TBMU = ((rx_frame.data.u8[3] & 0x40) >> 7);
|
||||
TBCU_48V_WAKEUP = (rx_frame.data.u8[3] >> 7);
|
||||
HV_BATT_MAX_REAL_CURR = (rx_frame.data.u8[5] << 7) | (rx_frame.data.u8[6] >> 1); //A -2000 - 2000 0.1 scaling
|
||||
TBMU_FAULT_TYPE = (rx_frame.data.u8[7] & 0xE0) >> 5;
|
||||
/*000: No fault
|
||||
001: FirstLevelFault: Warning Lamp
|
||||
010: SecondLevelFault: Stop Lamp
|
||||
011: ThirdLevelFault: Stop Lamp + contactor opening (EPS shutdown)
|
||||
100: FourthLevelFault: Stop Lamp + Active Discharge
|
||||
101: Inhibition of powertrain activation
|
||||
110: Reserved
|
||||
111: Invalid*/
|
||||
HV_BATT_REAL_VOLT_HD = ((rx_frame.data.u8[3] & 0x3F) << 8) | (rx_frame.data.u8[4]); //V 0-1000 * 0.1 scaling
|
||||
HV_BATT_REAL_CURR_HD = (rx_frame.data.u8[1] << 8) | (rx_frame.data.u8[2]); //A -2000 - 2000 0.1 scaling
|
||||
CHECKSUM_FRAME_373 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xD
|
||||
COUNTER_373 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x4F4: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_CRASH_MEMORIZED = ((rx_frame.data.u8[2] & 0x08) >> 3);
|
||||
HV_BATT_COLD_CRANK_ACK = ((rx_frame.data.u8[2] & 0x04) >> 2);
|
||||
HV_BATT_CHARGE_NEEDED_STATE = ((rx_frame.data.u8[2] & 0x02) >> 1);
|
||||
HV_BATT_NOM_CH_VOLTAGE = ((rx_frame.data.u8[2] & 0x01) << 8) | (rx_frame.data.u8[3]); //V 0 - 500
|
||||
HV_BATT_NOM_CH_CURRENT = rx_frame.data.u8[4]; // -120 - 0 0.5scaling
|
||||
HV_BATT_GENERATED_HEAT_RATE = (rx_frame.data.u8[5] << 1) | (rx_frame.data.u8[6] >> 7); //W 0-50000
|
||||
REQ_MIL_LAMP_CONTINOUS = (rx_frame.data.u8[7] & 0x04) >> 2;
|
||||
REQ_BLINK_STOP_AND_SERVICE_LAMP = (rx_frame.data.u8[7] & 0x02) >> 1;
|
||||
CMD_RESET_MIL = (rx_frame.data.u8[7] & 0x01);
|
||||
HV_BATT_SOC = (rx_frame.data.u8[1] << 2) | (rx_frame.data.u8[2] >> 6);
|
||||
CONTACTORS_STATE =
|
||||
(rx_frame.data.u8[2] & 0x30) >> 4; //00 : contactor open 01 : pre-load contactor 10 : contactor close
|
||||
HV_BATT_DISCONT_WARNING_OPEN = (rx_frame.data.u8[7] & 0x08) >> 3;
|
||||
CHECKSUM_FRAME_4F4 = (rx_frame.data.u8[0] & 0xF0) >> 4;
|
||||
COUNTER_4F4 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x414: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_REAL_POWER_HD = (rx_frame.data.u8[1] << 7) | (rx_frame.data.u8[2] >> 1);
|
||||
MAX_ALLOW_CHRG_POWER =
|
||||
((rx_frame.data.u8[2] & 0x01) << 13) | (rx_frame.data.u8[3] << 5) | ((rx_frame.data.u8[4] & 0xF8) >> 3);
|
||||
MAX_ALLOW_DISCHRG_POWER =
|
||||
((rx_frame.data.u8[5] & 0x07) << 11) | (rx_frame.data.u8[6] << 3) | ((rx_frame.data.u8[7] & 0xE0) >> 5);
|
||||
CHECKSUM_FRAME_414 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x9
|
||||
COUNTER_414 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x353: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_COP_VOLTAGE =
|
||||
(rx_frame.data.u8[1] << 5) | (rx_frame.data.u8[2] >> 3); //Real voltage HV battery (dV, 0-5000)
|
||||
HV_BATT_COP_CURRENT =
|
||||
(rx_frame.data.u8[3] << 5) | (rx_frame.data.u8[4] >> 3); //High resolution battery current (dA, -4000 - 4000)
|
||||
CHECKSUM_FRAME_353 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xB
|
||||
COUNTER_353 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x474: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
BMS_DC_RELAY_MES_EVSE_VOLTAGE = (rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[2] >> 2); //V 0-1000
|
||||
FAST_CHARGE_CONTACTOR_STATE = (rx_frame.data.u8[2] & 0x03);
|
||||
/*00: Contactors Opened
|
||||
01: Contactors Closed
|
||||
10: No Request
|
||||
11: WELDING TEST*/
|
||||
BMS_FASTCHARGE_STATUS = (rx_frame.data.u8[4] & 0x03);
|
||||
/*00 : not charging
|
||||
01 : charging
|
||||
10 : charging fault
|
||||
11 : charging finished*/
|
||||
CHECKSUM_FRAME_474 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xF
|
||||
COUNTER_474 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x574: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_FC_INSU_MINUS_RES = (rx_frame.data.u8[0] << 5) | (rx_frame.data.u8[1] >> 3); //kOhm (0-60000)
|
||||
HV_BATT_FC_VHL_INSU_PLUS_RES =
|
||||
((rx_frame.data.u8[1] & 0x07) << 10) | (rx_frame.data.u8[2] << 2) | ((rx_frame.data.u8[3] & 0xC0) >> 6);
|
||||
HV_BATT_FC_INSU_PLUS_RES = (rx_frame.data.u8[5] << 4) | (rx_frame.data.u8[6] >> 4);
|
||||
HV_BATT_ONLY_INSU_MINUS_RES = ((rx_frame.data.u8[3] & 0x3F) << 7) | (rx_frame.data.u8[4] >> 1);
|
||||
break;
|
||||
case 0x583: //MysteryVan 50/75kWh platform (CAN-FD also?)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
ALERT_OVERCHARGE = (rx_frame.data.u8[4] & 0x20) >> 5;
|
||||
NUMBER_PROBE_TEMP_MAX = rx_frame.data.u8[0];
|
||||
NUMBER_PROBE_TEMP_MIN = rx_frame.data.u8[1];
|
||||
TEMPERATURE_MINIMUM_C = rx_frame.data.u8[2] - 40;
|
||||
ALERT_BATT = (rx_frame.data.u8[3] & 0x80) >> 7;
|
||||
ALERT_TEMP_DIFF = (rx_frame.data.u8[3] & 0x40) >> 6;
|
||||
ALERT_HIGH_TEMP = (rx_frame.data.u8[3] & 0x20) >> 5;
|
||||
ALERT_OVERVOLTAGE = (rx_frame.data.u8[3] & 0x10) >> 4;
|
||||
ALERT_LOW_SOC = (rx_frame.data.u8[3] & 0x08) >> 3;
|
||||
ALERT_HIGH_SOC = (rx_frame.data.u8[3] & 0x04) >> 2;
|
||||
ALERT_CELL_OVERVOLTAGE = (rx_frame.data.u8[3] & 0x02) >> 1;
|
||||
ALERT_CELL_UNDERVOLTAGE = (rx_frame.data.u8[3] & 0x01);
|
||||
ALERT_SOC_JUMP = (rx_frame.data.u8[4] & 0x80) >> 7;
|
||||
ALERT_CELL_POOR_CONSIST = (rx_frame.data.u8[4] & 0x40) >> 6;
|
||||
CONTACTOR_OPENING_REASON = (rx_frame.data.u8[4] & 0x1C) >> 2;
|
||||
/*
|
||||
000 : Not error
|
||||
001 : Crash
|
||||
010 : 12V supply source undervoltage
|
||||
011 : 12V supply source overvoltage
|
||||
100 : Battery temperature
|
||||
101 : interlock line open
|
||||
110 : e-Service plug disconnected
|
||||
111 : Not valid
|
||||
*/
|
||||
NUMBER_OF_TEMPERATURE_SENSORS_IN_BATTERY = rx_frame.data.u8[5];
|
||||
NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY = rx_frame.data.u8[6];
|
||||
|
||||
break;
|
||||
case 0x314: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
MIN_ALLOW_DISCHRG_VOLTAGE = (rx_frame.data.u8[1] << 3) | (rx_frame.data.u8[2] >> 5); //V (0-1000)
|
||||
//EVSE_DC_MAX_CURRENT = ((rx_frame.data.u8[2] & 0x1F) << 5) | (rx_frame.data.u8[3] >> 3); //Fastcharger info, not needed for BE
|
||||
//TBMU_EVSE_DC_MAX_VOLTAGE //Fastcharger info, not needed for BE
|
||||
//TBMU_MAX_CHRG_SCKT_TEMP //Fastcharger info, not needed for BE
|
||||
//DC_CHARGE_MODE_AVAIL //Fastcharger info, not needed for BE
|
||||
//BIDIR_V2HG_MODE_AVAIL //Fastcharger info, not needed for BE
|
||||
//TBMU_CHRG_CONN_CONF //Fastcharger info, not needed for BE
|
||||
//EVSE_GRID_FAULT //Fastcharger info, not needed for BE
|
||||
CHECKSUM_FRAME_314 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x8
|
||||
COUNTER_314 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x254: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//HV_BATT_SOE_MAX_HR = frame6 & frame7 //Only on FD-CAN variant of the message. FD has length 7, non-fd 5
|
||||
HV_BATT_NOMINAL_DISCH_CURR_HD = (rx_frame.data.u8[0] << 7) | (rx_frame.data.u8[1] >> 1); //dA (0-20000)
|
||||
HV_BATT_PEAK_DISCH_CURR_HD = (rx_frame.data.u8[2] << 7) | (rx_frame.data.u8[3] >> 1); //dA (0-20000)
|
||||
HV_BATT_STABLE_DISCH_CURR_HD = (rx_frame.data.u8[4] << 7) | (rx_frame.data.u8[5] >> 1); //dA (0-20000)
|
||||
break;
|
||||
case 0x2B4: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_NOMINAL_CHARGE_CURR_HD = (rx_frame.data.u8[0] << 7) | (rx_frame.data.u8[1] >> 1);
|
||||
HV_BATT_PEAK_CHARGE_CURR_HD = (rx_frame.data.u8[2] << 7) | (rx_frame.data.u8[3] >> 1);
|
||||
HV_BATT_STABLE_CHARGE_CURR_HD = (rx_frame.data.u8[4] << 7) | (rx_frame.data.u8[5] >> 1);
|
||||
break;
|
||||
case 0x4D4: //MysteryVan 50/75kWh platform
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
HV_BATT_STABLE_CHARGE_POWER_HD = (rx_frame.data.u8[0] << 6) | (rx_frame.data.u8[1] >> 2);
|
||||
HV_BATT_STABLE_DISCH_POWER_HD =
|
||||
((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 4) | ((rx_frame.data.u8[4] & 0xF0) >> 4);
|
||||
HV_BATT_NOMINAL_DISCH_POWER_HD =
|
||||
((rx_frame.data.u8[4] & 0x0F) << 10) | (rx_frame.data.u8[5] << 2) | ((rx_frame.data.u8[6] & 0xC0) >> 6);
|
||||
MAX_ALLOW_DISCHRG_CURRENT = ((rx_frame.data.u8[6] & 0x3F) << 5) | (rx_frame.data.u8[7] >> 3);
|
||||
RC01_PERM_SYNTH_TBMU = (rx_frame.data.u8[7] & 0x04) >> 2; //TBMU Readiness Code synthesis
|
||||
CHECKSUM_FRAME_4D4 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x5
|
||||
COUNTER_4D4 = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x125: //Common eCMP
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_soc = (rx_frame.data.u8[0] << 2) |
|
||||
(rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt)
|
||||
battery_MainConnectorState = ((rx_frame.data.u8[2] & 0x18) >>
|
||||
|
@ -157,6 +460,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_current = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]) - 600; // TODO: Test
|
||||
break;
|
||||
case 0x127: //DFM specific
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_AllowedMaxChargeCurrent =
|
||||
(rx_frame.data.u8[0] << 2) |
|
||||
((rx_frame.data.u8[1] & 0xC0) >> 6); //Byte 1, bit 7, length 10 (0-600A) [0x3FF if invalid]
|
||||
|
@ -165,12 +469,15 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
(rx_frame.data.u8[3] >> 4); //Byte 2, bit 5, length 10 (0-600A) [0x3FF if invalid]
|
||||
break;
|
||||
case 0x129: //PSA specific
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x31B:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_InterlockOpen = ((rx_frame.data.u8[1] & 0x10) >> 4); //Best guess, seems to work?
|
||||
//TODO: frame7 contains checksum, we can use this to check for CAN message corruption
|
||||
break;
|
||||
case 0x358: //Common
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_highestTemperature = rx_frame.data.u8[6] - 40;
|
||||
battery_lowestTemperature = rx_frame.data.u8[7] - 40;
|
||||
break;
|
||||
|
@ -185,11 +492,13 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 0x494:
|
||||
break;
|
||||
case 0x594:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_insulation_failure_diag = ((rx_frame.data.u8[6] & 0xE0) >> 5); //Unsure if this is right position
|
||||
//byte pos 6, bit pos 7, signal lenth 3
|
||||
//0 = no failure, 1 = symmetric failure, 4 = invalid value , forbidden value 5-7
|
||||
break;
|
||||
case 0x6D0: //Common
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_insulationResistanceKOhm =
|
||||
(rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; //Byte 2, bit 7, length 16 (0-60000 kOhm)
|
||||
break;
|
||||
|
@ -198,6 +507,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 0x6D2:
|
||||
break;
|
||||
case 0x6D3:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
cellvoltages[0] = (rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1];
|
||||
cellvoltages[1] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
|
||||
cellvoltages[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||
|
@ -374,6 +684,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, 108 * sizeof(uint16_t));
|
||||
break;
|
||||
case 0x694: // Poll reply
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
// Handle user requested functionality first if ongoing
|
||||
if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "CanBattery.h"
|
||||
#include "ECMP-HTML.h"
|
||||
|
||||
#ifdef STELLANTIS_ECMP_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS EcmpBattery
|
||||
#endif
|
||||
|
||||
class EcmpBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -42,6 +38,7 @@ class EcmpBattery : public CanBattery {
|
|||
static const int COMPLETED_STATE = 0;
|
||||
bool battery_RelayOpenRequest = false;
|
||||
bool battery_InterlockOpen = false;
|
||||
bool MysteryVan = false;
|
||||
uint8_t ContactorResetStatemachine = 0;
|
||||
uint8_t CollisionResetStatemachine = 0;
|
||||
uint8_t IsolationResetStatemachine = 0;
|
||||
|
@ -84,8 +81,8 @@ class EcmpBattery : public CanBattery {
|
|||
uint8_t pid_coldest_module = NOT_SAMPLED_YET;
|
||||
uint8_t pid_lowest_temperature = NOT_SAMPLED_YET;
|
||||
uint8_t pid_average_temperature = NOT_SAMPLED_YET;
|
||||
uint8_t pid_highest_temperature = NOT_SAMPLED_YET;
|
||||
uint8_t pid_hottest_module = NOT_SAMPLED_YET;
|
||||
int8_t pid_highest_temperature = NOT_SAMPLED_YET;
|
||||
int8_t pid_hottest_module = NOT_SAMPLED_YET;
|
||||
uint16_t pid_avg_cell_voltage = NOT_SAMPLED_YET;
|
||||
int32_t pid_current = NOT_SAMPLED_YET;
|
||||
uint32_t pid_insulation_res_neg = NOT_SAMPLED_YET;
|
||||
|
@ -136,7 +133,68 @@ class EcmpBattery : public CanBattery {
|
|||
uint32_t pid_date_of_manufacture = NOT_SAMPLED_YET;
|
||||
uint16_t pid_SOH_cell_1 = NOT_SAMPLED_YET;
|
||||
|
||||
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
|
||||
//MysteryVan platform (allcaps to make code easer to co-exist)
|
||||
uint16_t SOE_MAX_CURRENT_TEMP = 0;
|
||||
uint16_t FRONT_MACHINE_POWER_LIMIT = 0;
|
||||
uint16_t REAR_MACHINE_POWER_LIMIT = 0;
|
||||
uint16_t EVSE_INSTANT_DC_HV_CURRENT = 0;
|
||||
uint8_t EVSE_STATE = 0;
|
||||
uint16_t HV_BATT_SOE_HD = 0;
|
||||
uint16_t HV_BATT_SOE_MAX = 0;
|
||||
uint8_t CHECKSUM_FRAME_314, CHECKSUM_FRAME_3B4, CHECKSUM_FRAME_554, CHECKSUM_FRAME_373, CHECKSUM_FRAME_4F4,
|
||||
CHECKSUM_FRAME_414, CHECKSUM_FRAME_353, CHECKSUM_FRAME_474, CHECKSUM_FRAME_4D4 = 0;
|
||||
uint16_t HV_STORAGE_MAX_I = 0;
|
||||
int8_t BMS_PROBETEMP[7] = {0};
|
||||
uint8_t COUNTER_314, COUNTER_554, COUNTER_373, COUNTER_3B4, COUNTER_4F4, COUNTER_414, COUNTER_353, COUNTER_474,
|
||||
COUNTER_4D4 = 0;
|
||||
uint16_t HV_BATT_PEAK_DISCH_POWER_HD = 0;
|
||||
uint16_t HV_BATT_PEAK_CH_POWER_HD = 0;
|
||||
uint16_t HV_BATT_NOM_CH_POWER_HD = 0;
|
||||
uint16_t MAX_ALLOW_CHRG_CURRENT = 0;
|
||||
int16_t HV_BATT_REAL_CURR_HD = 0;
|
||||
uint16_t HV_BATT_REAL_VOLT_HD = 0;
|
||||
uint8_t TBMU_FAULT_TYPE = 0;
|
||||
int16_t HV_BATT_MAX_REAL_CURR = 0;
|
||||
bool TBCU_48V_WAKEUP = false;
|
||||
bool REQ_CLEAR_DTC_TBMU = false;
|
||||
bool HV_BATT_DISCONT_WARNING_OPEN = false;
|
||||
uint8_t CONTACTORS_STATE = 0;
|
||||
uint16_t HV_BATT_SOC = 0;
|
||||
bool CMD_RESET_MIL = 0;
|
||||
bool REQ_BLINK_STOP_AND_SERVICE_LAMP = false;
|
||||
bool REQ_MIL_LAMP_CONTINOUS = false;
|
||||
uint16_t HV_BATT_GENERATED_HEAT_RATE = 0;
|
||||
bool HV_BATT_CRASH_MEMORIZED = false;
|
||||
bool HV_BATT_COLD_CRANK_ACK = false;
|
||||
bool HV_BATT_CHARGE_NEEDED_STATE = false;
|
||||
uint8_t HV_BATT_NOM_CH_CURRENT = 0;
|
||||
uint16_t HV_BATT_NOM_CH_VOLTAGE = 0;
|
||||
uint16_t HV_BATT_REAL_POWER_HD = 0;
|
||||
uint16_t MAX_ALLOW_CHRG_POWER = 0;
|
||||
uint16_t MAX_ALLOW_DISCHRG_POWER = 0;
|
||||
bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP,
|
||||
ALERT_TEMP_DIFF, ALERT_HIGH_TEMP, ALERT_OVERVOLTAGE, ALERT_CELL_OVERVOLTAGE, ALERT_CELL_UNDERVOLTAGE = false;
|
||||
uint8_t NUMBER_PROBE_TEMP_MAX, NUMBER_PROBE_TEMP_MIN = 0;
|
||||
int8_t TEMPERATURE_MINIMUM_C = 0;
|
||||
uint8_t CONTACTOR_OPENING_REASON = 0;
|
||||
uint8_t NUMBER_OF_TEMPERATURE_SENSORS_IN_BATTERY, NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY = 0;
|
||||
uint16_t HV_BATT_COP_VOLTAGE = 0;
|
||||
int16_t HV_BATT_COP_CURRENT = 0;
|
||||
uint16_t BMS_DC_RELAY_MES_EVSE_VOLTAGE = 0;
|
||||
uint8_t FAST_CHARGE_CONTACTOR_STATE = 0;
|
||||
uint8_t BMS_FASTCHARGE_STATUS = 0;
|
||||
uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES,
|
||||
HV_BATT_ONLY_INSU_MINUS_RES = 0;
|
||||
uint16_t MIN_ALLOW_DISCHRG_VOLTAGE = 0;
|
||||
uint16_t HV_BATT_NOMINAL_DISCH_CURR_HD, HV_BATT_PEAK_DISCH_CURR_HD, HV_BATT_STABLE_DISCH_CURR_HD = 0;
|
||||
uint16_t HV_BATT_NOMINAL_CHARGE_CURR_HD, HV_BATT_PEAK_CHARGE_CURR_HD, HV_BATT_STABLE_CHARGE_CURR_HD = 0;
|
||||
bool RC01_PERM_SYNTH_TBMU = false;
|
||||
uint16_t HV_BATT_STABLE_CHARGE_POWER_HD = 0;
|
||||
uint16_t HV_BATT_STABLE_DISCH_POWER_HD = 0;
|
||||
uint16_t HV_BATT_NOMINAL_DISCH_POWER_HD = 0;
|
||||
uint16_t MAX_ALLOW_DISCHRG_CURRENT = 0;
|
||||
|
||||
unsigned long previousMillis10 = 0; //- will store last time a 10ms CAN Message was sent
|
||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent
|
||||
unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
|
||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
|
||||
|
|
|
@ -383,6 +383,132 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer {
|
|||
: String(datalayer_extended.stellantisECMP.pid_SOH_cell_1)) +
|
||||
"</h4>";
|
||||
|
||||
if (datalayer_extended.stellantisECMP.MysteryVan) {
|
||||
content += "<h3>MysteryVan platform detected!</h3>";
|
||||
content += "<h4>Contactor State: ";
|
||||
if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 0) {
|
||||
content += "Open";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 1) {
|
||||
content += "Precharge";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 2) {
|
||||
content += "Closed";
|
||||
}
|
||||
content += "</h4>";
|
||||
content += "<h4>Crash Memorized: ";
|
||||
if (datalayer_extended.stellantisECMP.CrashMemorized) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Contactor Opening Reason: ";
|
||||
if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 0) {
|
||||
content += "No error";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 1) {
|
||||
content += "Crash!";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 2) {
|
||||
content += "12V supply source undervoltage";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 3) {
|
||||
content += "12V supply source overvoltage";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 4) {
|
||||
content += "Battery temperature";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 5) {
|
||||
content += "Interlock line open";
|
||||
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 6) {
|
||||
content += "e-Service plug disconnected";
|
||||
}
|
||||
content += "</h4>";
|
||||
content += "<h4>Battery fault type: ";
|
||||
if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 0) {
|
||||
content += "No fault";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 1) {
|
||||
content += "FirstLevelFault: Warning Lamp";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 2) {
|
||||
content += "SecondLevelFault: Stop Lamp";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 3) {
|
||||
content += "ThirdLevelFault: Stop Lamp + contactor opening (EPS shutdown)";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 4) {
|
||||
content += "FourthLevelFault: Stop Lamp + Active Discharge";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 5) {
|
||||
content += "Inhibition of powertrain activation";
|
||||
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 6) {
|
||||
content += "Reserved";
|
||||
}
|
||||
content += "</h4>";
|
||||
content += "<h4>FC insulation minus resistance " +
|
||||
String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES) + " kOhm</h4>";
|
||||
content += "<h4>FC insulation plus resistance " +
|
||||
String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES) + " kOhm</h4>";
|
||||
content += "<h4>FC vehicle insulation plus resistance " +
|
||||
String(datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES) + " kOhm</h4>";
|
||||
content += "<h4>FC vehicle insulation plus resistance " +
|
||||
String(datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES) + " kOhm</h4>";
|
||||
}
|
||||
content += "<h4>Alert Battery: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_BATT) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Low SOC: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_LOW_SOC) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert High SOC: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_HIGH_SOC) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert SOC Jump: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_SOC_JUMP) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Overcharge: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_OVERCHARGE) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Temp Diff: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Temp High: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Overvoltage: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Cell Overvoltage: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Cell Undervoltage: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
content += "<h4>Alert Cell Poor Consistency: ";
|
||||
if (datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST) {
|
||||
content += "Yes</h4>";
|
||||
} else {
|
||||
content += "No</h4>";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "FOXESS-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#define FOXESS_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef FOXESS_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS FoxessBattery
|
||||
#endif
|
||||
|
||||
class FoxessBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "GEELY-GEOMETRY-C-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
/* TODO
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
#ifndef GEELY_GEOMETRY_C_BATTERY_H
|
||||
#define GEELY_GEOMETRY_C_BATTERY_H
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "CanBattery.h"
|
||||
#include "GEELY-GEOMETRY-C-HTML.h"
|
||||
|
||||
#ifdef GEELY_GEOMETRY_C_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
|
||||
#endif
|
||||
|
||||
class GeelyGeometryCBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
GeelyGeometryCBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_GEELY_GEOMETRY_C* extended,
|
||||
CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
|
||||
battery_voltage = 0;
|
||||
}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
GeelyGeometryCBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
datalayer_geometryc = &datalayer_extended.geometryC;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -20,6 +29,11 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
GeelyGeometryCHtmlRenderer renderer;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
|
||||
|
||||
static const int POLL_SOC = 0x4B35;
|
||||
static const int POLL_CC2_VOLTAGE = 0x4BCF;
|
||||
static const int POLL_CELL_MAX_VOLTAGE_NUMBER = 0x4B1E;
|
||||
|
@ -41,8 +55,6 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
static const int POLL_MULTI_HARDWARE_VERSION = 0x4B6B;
|
||||
static const int POLL_MULTI_SOFTWARE_VERSION = 0x4B6C;
|
||||
|
||||
GeelyGeometryCHtmlRenderer renderer;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh
|
||||
static const int MIN_PACK_VOLTAGE_70_DV = 2860;
|
||||
static const int MAX_PACK_VOLTAGE_53_DV = 4160; //53kWh
|
||||
|
@ -51,9 +63,6 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
|
||||
|
||||
CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#ifndef _GEELY_GEOMETRY_C_HTML_H
|
||||
#define _GEELY_GEOMETRY_C_HTML_H
|
||||
|
||||
#include <cstring>
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
|
@ -10,7 +9,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
|
|||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
char readableSerialNumber[29]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.geometryC.BatterySerialNumber,
|
||||
sizeof(datalayer_extended.geometryC.BatterySerialNumber));
|
||||
|
@ -52,7 +50,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
|
|||
"<h4>Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " °C</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,9 +5,7 @@ String HyundaiIoniq28BatteryHtmlRenderer::get_status_html() {
|
|||
String content;
|
||||
content += "<h4>12V voltage: " + String(batt.get_lead_acid_voltage() / 10.0, 1) + "</h4>";
|
||||
content += "<h4>Temperature, power relay: " + String(batt.get_power_relay_temperature()) + "</h4>";
|
||||
content += "<h4>Battery relay: " + String(batt.get_battery_relay_mode()) + "</h4>";
|
||||
content += "<h4>Batterymanagement mode: " + String(batt.get_battery_management_mode()) + "</h4>";
|
||||
content += "<h4>BMS ignition: " + String(batt.get_battery_ignition_mode()) + "</h4>";
|
||||
|
||||
content += "<h4>Isolation resistance: " + String(batt.get_isolation_resistance()) + " kOhm</h4>";
|
||||
return content;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,11 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x21: //First frame in PID group
|
||||
if (incoming_poll_group == 1) { //21 14 13 24 13 24 04 00
|
||||
batteryRelay = rx_frame.data.u8[7];
|
||||
//SOC = rx_frame.data.u8[1];
|
||||
//available_charge_power = 2 & 3
|
||||
//available_discharge_power = 4 & 5
|
||||
//status_bits? = 6
|
||||
//battery_current_highbyte = rx_frame.data.u8[7];
|
||||
} else if (incoming_poll_group == 2) { //21 AD AD AD AD AD AD AC
|
||||
cellvoltages_mv[0] = (rx_frame.data.u8[1] * 20);
|
||||
cellvoltages_mv[1] = (rx_frame.data.u8[2] * 20);
|
||||
|
@ -113,11 +117,19 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
cellvoltages_mv[68] = (rx_frame.data.u8[5] * 20);
|
||||
cellvoltages_mv[69] = (rx_frame.data.u8[6] * 20);
|
||||
cellvoltages_mv[70] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (incoming_poll_group == 5) { //21 0 0 0 0 0 0f0f
|
||||
//battery_module_6_temperature = rx_frame.data.u8[6];
|
||||
//battery_module_7_temperature = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x22: //Second datarow in PID group
|
||||
if (incoming_poll_group == 1) { //22 00 0C FF 17 16 17 17
|
||||
|
||||
//battery_current_lowbyte = rx_frame.data.u8[1];
|
||||
//battery_DC_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
//battery_max_temperature = rx_frame.data.u8[4];
|
||||
//battery_min_temperature = rx_frame.data.u8[5];
|
||||
//battery_module_1_temperature = rx_frame.data.u8[6];
|
||||
//battery_module_2_temperature = rx_frame.data.u8[7];
|
||||
} else if (incoming_poll_group == 2) { //22 AD AC AC AD AD AD AD
|
||||
cellvoltages_mv[7] = (rx_frame.data.u8[1] * 20);
|
||||
cellvoltages_mv[8] = (rx_frame.data.u8[2] * 20);
|
||||
|
@ -142,14 +154,26 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
cellvoltages_mv[75] = (rx_frame.data.u8[5] * 20);
|
||||
cellvoltages_mv[76] = (rx_frame.data.u8[6] * 20);
|
||||
cellvoltages_mv[77] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (incoming_poll_group == 5) { //22 10 0d 0c 0e 0d 26 48
|
||||
//battery_module_8_temperature = rx_frame.data.u8[1];
|
||||
//battery_module_9_temperature = rx_frame.data.u8[2];
|
||||
//battery_module_10_temperature = rx_frame.data.u8[3];
|
||||
//battery_module_11_temperature = rx_frame.data.u8[4];
|
||||
//battery_module_12_temperature = rx_frame.data.u8[5];
|
||||
//available_charge_power = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
} else if (incoming_poll_group == 6) {
|
||||
batteryManagementMode = rx_frame.data.u8[5];
|
||||
}
|
||||
break;
|
||||
case 0x23: //Third datarow in PID group
|
||||
if (incoming_poll_group == 1) { //23 17 17 17 00 17 AD 25
|
||||
case 0x23: //Third datarow in PID group
|
||||
if (incoming_poll_group == 1) { //23 17 17 17 00 17 AD 25
|
||||
//battery_module_3_temperature = rx_frame.data.u8[1];
|
||||
//battery_module_4_temperature = rx_frame.data.u8[2];
|
||||
//battery_module_5_temperature = rx_frame.data.u8[3];
|
||||
//battery_inlet_temperature = rx_frame.data.u8[5];
|
||||
CellVoltMax_mV = (rx_frame.data.u8[6] * 20); //(volts *50) *20 =mV
|
||||
} else if (incoming_poll_group == 2) { //23 AD AD AD AD AB AD AD
|
||||
//cellmaxvoltage_number = rx_frame.data.u8[7];
|
||||
} else if (incoming_poll_group == 2) { //23 AD AD AD AD AB AD AD
|
||||
cellvoltages_mv[14] = (rx_frame.data.u8[1] * 20);
|
||||
cellvoltages_mv[15] = (rx_frame.data.u8[2] * 20);
|
||||
cellvoltages_mv[16] = (rx_frame.data.u8[3] * 20);
|
||||
|
@ -174,13 +198,22 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
cellvoltages_mv[83] = (rx_frame.data.u8[6] * 20);
|
||||
cellvoltages_mv[84] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (incoming_poll_group == 5) {
|
||||
heatertemp = rx_frame.data.u8[7];
|
||||
//available_discharge_power = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
|
||||
//battery_cell_mV_deviation = rx_frame.data.u8[3];
|
||||
//airbag_h/wire_duty = rx_frame.data.u8[5];
|
||||
heatertemperature_1 = rx_frame.data.u8[6];
|
||||
heatertemperature_2 = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x24: //Fourth datarow in PID group
|
||||
if (incoming_poll_group == 1) { //24 AA 4F 00 00 77 00 14
|
||||
CellVoltMin_mV = (rx_frame.data.u8[1] * 20); //(volts *50) *20 =mV
|
||||
} else if (incoming_poll_group == 2) { //24 AD AD AD AD AD AD AB
|
||||
//mincellvoltage_number = rx_frame.data.u8[2];
|
||||
//fan_status = rx_frame.data.u8[3];
|
||||
//fan_feedback_signal = rx_frame.data.u8[4];
|
||||
//aux_battery_voltage = rx_frame.data.u8[5];
|
||||
//cumulative_charge_current_highbyte = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
} else if (incoming_poll_group == 2) { //24 AD AD AD AD AD AD AB
|
||||
cellvoltages_mv[21] = (rx_frame.data.u8[1] * 20);
|
||||
cellvoltages_mv[22] = (rx_frame.data.u8[2] * 20);
|
||||
cellvoltages_mv[23] = (rx_frame.data.u8[3] * 20);
|
||||
|
@ -204,13 +237,19 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
cellvoltages_mv[89] = (rx_frame.data.u8[5] * 20);
|
||||
cellvoltages_mv[90] = (rx_frame.data.u8[6] * 20);
|
||||
cellvoltages_mv[91] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (incoming_poll_group == 5) {
|
||||
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
|
||||
} else if (incoming_poll_group == 5) { //24 3 e8 5 3 e8 m34 6e
|
||||
batterySOH = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
|
||||
//max_deterioration_cell_number = rx_frame.data.u8[3]
|
||||
//min_deterioration = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
//min_deterioration_cell_number = rx_frame.data.u8[6]
|
||||
//SOC_display = rx_frame.data.u8[7]
|
||||
}
|
||||
break;
|
||||
case 0x25: //Fifth datarow in PID group
|
||||
if (incoming_poll_group == 1) { //25 5C A9 00 14 5F D3 00
|
||||
|
||||
//cumulative_charge_current_lowbyte = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
|
||||
//cumulative_discharge_current = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[4] << 16) | (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
|
||||
//cumulative_charge_energy_highbyte = rx_frame.data.u8[7];
|
||||
} else if (incoming_poll_group == 2) { //25 AD AD AD AD 00 00 00
|
||||
cellvoltages_mv[28] = (rx_frame.data.u8[1] * 20);
|
||||
cellvoltages_mv[29] = (rx_frame.data.u8[2] * 20);
|
||||
|
@ -231,19 +270,22 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x26: //Sixth datarow in PID group
|
||||
if (incoming_poll_group == 1) { //26 07 84 F9 00 07 42 8F
|
||||
//cumulative_charge_energy_lowbyte = (rx_frame.data.u8[1] << 16) | (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
//cumulative_discharge_energy = ((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
|
||||
} else if (incoming_poll_group == 5) {
|
||||
}
|
||||
break;
|
||||
case 0x27: //Seventh datarow in PID group
|
||||
if (incoming_poll_group == 1) { //27 03 3F A1 EB 00 19 99
|
||||
BMS_ign = rx_frame.data.u8[6];
|
||||
inverterVoltageFrameHigh = rx_frame.data.u8[7];
|
||||
//cumulative_operating_time = ((rx_frame.data.u8[1] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
|
||||
//bitfield (41 off, 45 car on) = rx_frame.data.u8[5];
|
||||
inverterVoltage = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
}
|
||||
break;
|
||||
case 0x28: //Eighth datarow in PID group
|
||||
if (incoming_poll_group == 1) { //28 7F FF 7F FF 03 E8 00
|
||||
inverterVoltage = (inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1];
|
||||
isolation_resistance = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -378,10 +420,6 @@ uint8_t HyundaiIoniq28Battery::get_battery_management_mode() const {
|
|||
return batteryManagementMode;
|
||||
}
|
||||
|
||||
uint8_t HyundaiIoniq28Battery::get_battery_ignition_mode() const {
|
||||
return BMS_ign;
|
||||
}
|
||||
|
||||
uint8_t HyundaiIoniq28Battery::get_battery_relay_mode() const {
|
||||
return batteryRelay;
|
||||
uint16_t HyundaiIoniq28Battery::get_isolation_resistance() const {
|
||||
return isolation_resistance;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
#include "CanBattery.h"
|
||||
#include "HYUNDAI-IONIQ-28-BATTERY-HTML.h"
|
||||
|
||||
#ifdef HYUNDAI_IONIQ_28_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS HyundaiIoniq28Battery
|
||||
#endif
|
||||
|
||||
class HyundaiIoniq28Battery : public CanBattery {
|
||||
public:
|
||||
HyundaiIoniq28Battery() : renderer(*this) {}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
HyundaiIoniq28Battery() : renderer(*this) { datalayer_battery = &datalayer.battery; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
|
@ -25,10 +22,9 @@ class HyundaiIoniq28Battery : public CanBattery {
|
|||
|
||||
// Getter methods for HTML renderer
|
||||
uint16_t get_lead_acid_voltage() const;
|
||||
uint16_t get_isolation_resistance() const;
|
||||
int16_t get_power_relay_temperature() const;
|
||||
uint8_t get_battery_management_mode() const;
|
||||
uint8_t get_battery_ignition_mode() const;
|
||||
uint8_t get_battery_relay_mode() const;
|
||||
|
||||
private:
|
||||
HyundaiIoniq28BatteryHtmlRenderer renderer;
|
||||
|
@ -53,18 +49,17 @@ class HyundaiIoniq28Battery : public CanBattery {
|
|||
uint16_t allowedDischargePower = 0;
|
||||
uint16_t allowedChargePower = 0;
|
||||
uint16_t batteryVoltage = 3700;
|
||||
uint16_t inverterVoltageFrameHigh = 0;
|
||||
uint16_t inverterVoltage = 0;
|
||||
uint16_t isolation_resistance = 1000;
|
||||
uint16_t cellvoltages_mv[96];
|
||||
uint16_t leadAcidBatteryVoltage = 120;
|
||||
int16_t batteryAmps = 0;
|
||||
int16_t temperatureMax = 0;
|
||||
int16_t temperatureMin = 0;
|
||||
uint8_t batteryManagementMode = 0;
|
||||
uint8_t BMS_ign = 0;
|
||||
uint8_t batteryRelay = 0;
|
||||
uint8_t counter_200 = 0;
|
||||
int8_t heatertemp = 0;
|
||||
int8_t heatertemperature_1 = 0;
|
||||
int8_t heatertemperature_2 = 0;
|
||||
int8_t powerRelayTemperature = 0;
|
||||
bool startedUp = false;
|
||||
uint8_t incoming_poll_group = 0xFF;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "IMIEV-CZERO-ION-BATTERY.h"
|
||||
#include <cstring> //for unit tests
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -19,9 +20,11 @@ void ImievCZeroIonBattery::
|
|||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
||||
|
||||
//We do not know the max charge/discharge power is sent by the battery. We hardcode value for now.
|
||||
datalayer.battery.status.max_charge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
datalayer.battery.status.override_charge_power_W; //TODO: Fix when CAN is decoded
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
datalayer.battery.status.override_discharge_power_W; //TODO: Fix when CAN is decoded
|
||||
|
||||
static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]);
|
||||
max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array
|
||||
|
@ -75,30 +78,9 @@ void ImievCZeroIonBattery::
|
|||
}
|
||||
|
||||
if (!BMU_Detected) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BMU not detected, check wiring!");
|
||||
#endif
|
||||
//TODO: Raise event
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Battery Values");
|
||||
logging.print("BMU SOC: ");
|
||||
logging.print(BMU_SOC);
|
||||
logging.print(" BMU Current: ");
|
||||
logging.print(BMU_Current);
|
||||
logging.print(" BMU Battery Voltage: ");
|
||||
logging.print(BMU_PackVoltage);
|
||||
logging.print(" BMU_Power: ");
|
||||
logging.print(BMU_Power);
|
||||
logging.print(" Cell max voltage: ");
|
||||
logging.print(max_volt_cel);
|
||||
logging.print(" Cell min voltage: ");
|
||||
logging.print(min_volt_cel);
|
||||
logging.print(" Cell max temp: ");
|
||||
logging.print(max_temp_cel);
|
||||
logging.print(" Cell min temp: ");
|
||||
logging.println(min_temp_cel);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImievCZeroIonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#define IMIEV_CZERO_ION_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS ImievCZeroIonBattery
|
||||
#endif
|
||||
|
||||
class ImievCZeroIonBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "JAGUAR-IPACE-BATTERY.h"
|
||||
#include <cstring> //for unit tests
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -57,9 +58,9 @@ CAN_frame ipace_keep_alive = {.FD = false,
|
|||
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/
|
||||
|
||||
static void print_units(const char* header, int value, const char* units) {
|
||||
logging.print(header);
|
||||
logging.print(value);
|
||||
logging.print(units);
|
||||
logging.printf(header);
|
||||
logging.printf("%d", value);
|
||||
logging.printf(units);
|
||||
}
|
||||
|
||||
void JaguarIpaceBattery::update_values() {
|
||||
|
@ -102,21 +103,6 @@ void JaguarIpaceBattery::update_values() {
|
|||
} else {
|
||||
clear_event(EVENT_BATTERY_ISOLATION);
|
||||
}
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Values going to inverter");
|
||||
print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 0.01), "% ");
|
||||
print_units(", SOC%: ", (datalayer.battery.status.reported_soc * 0.01), "% ");
|
||||
print_units(", Voltage: ", (datalayer.battery.status.voltage_dV * 0.1), "V ");
|
||||
print_units(", Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W ");
|
||||
print_units(", Max charge power: ", datalayer.battery.status.max_charge_power_W, "W ");
|
||||
print_units(", Max temp: ", (datalayer.battery.status.temperature_max_dC * 0.1), "°C ");
|
||||
print_units(", Min temp: ", (datalayer.battery.status.temperature_min_dC * 0.1), "°C ");
|
||||
print_units(", Max cell voltage: ", datalayer.battery.status.cell_max_voltage_mV, "mV ");
|
||||
print_units(", Min cell voltage: ", datalayer.battery.status.cell_min_voltage_mV, "mV ");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
void JaguarIpaceBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef JAGUAR_IPACE_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS JaguarIpaceBattery
|
||||
#endif
|
||||
|
||||
class JaguarIpaceBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
423
Software/src/battery/KIA-64FD-BATTERY.cpp
Normal file
423
Software/src/battery/KIA-64FD-BATTERY.cpp
Normal file
|
@ -0,0 +1,423 @@
|
|||
#include "KIA-64FD-BATTERY.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "../devboard/utils/logging.h"
|
||||
#include "../system_settings.h"
|
||||
|
||||
// Function to estimate SOC based on cell voltage
|
||||
uint16_t Kia64FDBattery::estimateSOCFromCell(uint16_t cellVoltage) {
|
||||
if (cellVoltage >= voltage[0]) {
|
||||
return SOC[0];
|
||||
}
|
||||
if (cellVoltage <= voltage[numPoints - 1]) {
|
||||
return SOC[numPoints - 1];
|
||||
}
|
||||
|
||||
for (int i = 1; i < numPoints; ++i) {
|
||||
if (cellVoltage >= voltage[i]) {
|
||||
// Cast to float for proper division
|
||||
float t = (float)(cellVoltage - voltage[i]) / (float)(voltage[i - 1] - voltage[i]);
|
||||
|
||||
// Calculate interpolated SOC value
|
||||
uint16_t socDiff = SOC[i - 1] - SOC[i];
|
||||
uint16_t interpolatedValue = SOC[i] + (uint16_t)(t * socDiff);
|
||||
|
||||
return interpolatedValue;
|
||||
}
|
||||
}
|
||||
return 0; // Default return for safety, should never reach here
|
||||
}
|
||||
|
||||
// Simplified version of the pack-based SOC estimation with compensation
|
||||
uint16_t Kia64FDBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps) {
|
||||
|
||||
if (cellCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert pack voltage (decivolts) to millivolts
|
||||
uint32_t packVoltageMv = packVoltage * 100;
|
||||
|
||||
// Apply internal resistance compensation
|
||||
// Current is in deciamps (-150 = -15.0A, 150 = 15.0A)
|
||||
// Resistance is in milliohms
|
||||
int32_t voltageDrop = (currentAmps * PACK_INTERNAL_RESISTANCE_MOHM) / 10;
|
||||
|
||||
// Compensate the pack voltage (add the voltage drop)
|
||||
uint32_t compensatedPackVoltageMv = packVoltageMv + voltageDrop;
|
||||
|
||||
// Calculate average cell voltage in millivolts
|
||||
uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount;
|
||||
|
||||
// Use the cell voltage lookup table to estimate SOC
|
||||
return estimateSOCFromCell(avgCellVoltage);
|
||||
}
|
||||
|
||||
// Fix: Change parameter types to uint16_t to match SOC values
|
||||
uint16_t Kia64FDBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
||||
if (SOC_low == 0 || SOC_high == 0) {
|
||||
return 0; // If either value is 0, return 0
|
||||
}
|
||||
if (SOC_low == 10000 || SOC_high == 10000) {
|
||||
return 10000; // If either value is 100%, return 100%
|
||||
}
|
||||
return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value
|
||||
}
|
||||
|
||||
void write_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if ((rx_frame.data.u8[start + i] * 20) > 1000) {
|
||||
datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Kia64FDBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) {
|
||||
uint8_t crc = initial_value;
|
||||
for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC
|
||||
crc = crc8_table[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void Kia64FDBattery::update_values() {
|
||||
|
||||
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
|
||||
// Use the simplified pack-based SOC estimation with proper compensation
|
||||
datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
||||
|
||||
// For comparison or fallback, we can still calculate from min/max cell voltages
|
||||
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
||||
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
||||
#else
|
||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||
#endif
|
||||
|
||||
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
||||
|
||||
datalayer.battery.status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
|
||||
|
||||
datalayer.battery.status.current_dA = -batteryAmps; //value is *10 (150 = 15.0)
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
||||
|
||||
//datalayer.battery.status.max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
|
||||
//The allowed charge power is not available. We estimate this value for now
|
||||
if (datalayer.battery.status.real_soc > 9900) {
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else if (datalayer.battery.status.real_soc >
|
||||
RAMPDOWN_SOC) { // When real SOC is between 90-99%, ramp the value between Max<->0
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
RAMPDOWNPOWERALLOWED * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
|
||||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||
}
|
||||
|
||||
//datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
|
||||
//The allowed discharge power is not available. We hardcode this value for now
|
||||
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV;
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = CellVoltMin_mV;
|
||||
|
||||
if (leadAcidBatteryVoltage < 110) {
|
||||
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
|
||||
}
|
||||
}
|
||||
|
||||
void Kia64FDBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
startedUp = true;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x055:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x150:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x1F5:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x215:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x21A:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x235:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x245:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x25A:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x275:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x2FA:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x325:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x330:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x335:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x360:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x365:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x3BA:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x3F5:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x7EC:
|
||||
// print_canfd_frame(frame);
|
||||
switch (rx_frame.data.u8[0]) {
|
||||
case 0x10: //"PID Header"
|
||||
// logging.println ("Send ack");
|
||||
poll_data_pid = rx_frame.data.u8[4];
|
||||
// if (rx_frame.data.u8[4] == poll_data_pid) {
|
||||
transmit_can_frame(&KIA64FD_ack); //Send ack to BMS if the same frame is sent as polled
|
||||
// }
|
||||
break;
|
||||
case 0x21: //First frame in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
allowedChargePower = ((rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4]);
|
||||
allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]);
|
||||
SOC_BMS = rx_frame.data.u8[2] * 5; //100% = 200 ( 200 * 5 = 1000 )
|
||||
|
||||
} else if (poll_data_pid == 2) {
|
||||
// set cell voltages data, start bite, data length from start, start cell
|
||||
write_cell_voltages(rx_frame, 2, 6, 0);
|
||||
} else if (poll_data_pid == 3) {
|
||||
write_cell_voltages(rx_frame, 2, 6, 32);
|
||||
} else if (poll_data_pid == 4) {
|
||||
write_cell_voltages(rx_frame, 2, 6, 64);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
write_cell_voltages(rx_frame, 2, 6, 96);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
write_cell_voltages(rx_frame, 2, 6, 128);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
write_cell_voltages(rx_frame, 2, 6, 160);
|
||||
}
|
||||
break;
|
||||
case 0x22: //Second datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
batteryVoltage = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4];
|
||||
batteryAmps = (rx_frame.data.u8[1] << 8) + rx_frame.data.u8[2];
|
||||
temperatureMax = rx_frame.data.u8[5];
|
||||
temperatureMin = rx_frame.data.u8[6];
|
||||
// temp1 = rx_frame.data.u8[7];
|
||||
} else if (poll_data_pid == 2) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 6);
|
||||
} else if (poll_data_pid == 3) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 38);
|
||||
} else if (poll_data_pid == 4) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 70);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 102);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 134);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 166);
|
||||
} else if (poll_data_pid == 6) {
|
||||
batteryManagementMode = rx_frame.data.u8[5];
|
||||
}
|
||||
break;
|
||||
case 0x23: //Third datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
temperature_water_inlet = rx_frame.data.u8[6];
|
||||
CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
|
||||
// temp2 = rx_frame.data.u8[1];
|
||||
// temp3 = rx_frame.data.u8[2];
|
||||
// temp4 = rx_frame.data.u8[3];
|
||||
} else if (poll_data_pid == 2) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 13);
|
||||
} else if (poll_data_pid == 3) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 45);
|
||||
} else if (poll_data_pid == 4) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 77);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 109);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 141);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 173);
|
||||
} else if (poll_data_pid == 5) {
|
||||
// ac = rx_frame.data.u8[3];
|
||||
// Vdiff = rx_frame.data.u8[4];
|
||||
|
||||
// airbag = rx_frame.data.u8[6];
|
||||
heatertemp = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x24: //Fourth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
CellVmaxNo = rx_frame.data.u8[1];
|
||||
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
|
||||
CellVminNo = rx_frame.data.u8[3];
|
||||
// fanMod = rx_frame.data.u8[4];
|
||||
// fanSpeed = rx_frame.data.u8[5];
|
||||
leadAcidBatteryVoltage = rx_frame.data.u8[6]; //12v Battery Volts
|
||||
//cumulative_charge_current[0] = rx_frame.data.u8[7];
|
||||
} else if (poll_data_pid == 2) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 52);
|
||||
} else if (poll_data_pid == 4) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 84);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 116);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 148);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
write_cell_voltages(rx_frame, 1, 7, 180);
|
||||
} else if (poll_data_pid == 5) {
|
||||
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
|
||||
// maxDetCell = rx_frame.data.u8[4];
|
||||
// minDet = (rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6];
|
||||
// minDetCell = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x25: //Fifth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_charge_current[1] = rx_frame.data.u8[1];
|
||||
//cumulative_charge_current[2] = rx_frame.data.u8[2];
|
||||
//cumulative_charge_current[3] = rx_frame.data.u8[3];
|
||||
//cumulative_discharge_current[0] = rx_frame.data.u8[4];
|
||||
//cumulative_discharge_current[1] = rx_frame.data.u8[5];
|
||||
//cumulative_discharge_current[2] = rx_frame.data.u8[6];
|
||||
//cumulative_discharge_current[3] = rx_frame.data.u8[7];
|
||||
//set_cumulative_charge_current();
|
||||
//set_cumulative_discharge_current();
|
||||
} else if (poll_data_pid == 2) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 27);
|
||||
} else if (poll_data_pid == 3) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 59);
|
||||
} else if (poll_data_pid == 4) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 91);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 123);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 155);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
write_cell_voltages(rx_frame, 1, 5, 187);
|
||||
//set_cell_count();
|
||||
} else if (poll_data_pid == 5) {
|
||||
// datalayer.battery.info.number_of_cells = 98;
|
||||
SOC_Display = rx_frame.data.u8[1] * 5;
|
||||
}
|
||||
break;
|
||||
case 0x26: //Sixth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_energy_charged[0] = rx_frame.data.u8[1];
|
||||
// cumulative_energy_charged[1] = rx_frame.data.u8[2];
|
||||
//cumulative_energy_charged[2] = rx_frame.data.u8[3];
|
||||
//cumulative_energy_charged[3] = rx_frame.data.u8[4];
|
||||
//cumulative_energy_discharged[0] = rx_frame.data.u8[5];
|
||||
//cumulative_energy_discharged[1] = rx_frame.data.u8[6];
|
||||
//cumulative_energy_discharged[2] = rx_frame.data.u8[7];
|
||||
// set_cumulative_energy_charged();
|
||||
}
|
||||
break;
|
||||
case 0x27: //Seventh datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_energy_discharged[3] = rx_frame.data.u8[1];
|
||||
|
||||
//opTimeBytes[0] = rx_frame.data.u8[2];
|
||||
//opTimeBytes[1] = rx_frame.data.u8[3];
|
||||
//opTimeBytes[2] = rx_frame.data.u8[4];
|
||||
//opTimeBytes[3] = rx_frame.data.u8[5];
|
||||
|
||||
BMS_ign = rx_frame.data.u8[6];
|
||||
inverterVoltageFrameHigh = rx_frame.data.u8[7]; // BMS Capacitoir
|
||||
|
||||
// set_cumulative_energy_discharged();
|
||||
// set_opTime();
|
||||
}
|
||||
break;
|
||||
case 0x28: //Eighth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
inverterVoltage = (inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1]; // BMS Capacitoir
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Kia64FDBattery::transmit_can(unsigned long currentMillis) {
|
||||
if (startedUp) {
|
||||
//Send Contactor closing message loop
|
||||
// Check if we still have messages to send
|
||||
if (messageIndex < sizeof(messageDelays) / sizeof(messageDelays[0])) {
|
||||
|
||||
// Check if it's time to send the next message
|
||||
if (currentMillis - startMillis >= messageDelays[messageIndex]) {
|
||||
|
||||
// Transmit the current message
|
||||
transmit_can_frame(messages[messageIndex]);
|
||||
|
||||
// Move to the next message
|
||||
messageIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageIndex >= 63) {
|
||||
startMillis = currentMillis; // Start over!
|
||||
messageIndex = 0;
|
||||
}
|
||||
|
||||
//Send 200ms CANFD message
|
||||
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
|
||||
previousMillis200ms = currentMillis;
|
||||
|
||||
KIA64FD_7E4.data.u8[3] = KIA_7E4_COUNTER;
|
||||
|
||||
if (ok_start_polling_battery) {
|
||||
transmit_can_frame(&KIA64FD_7E4);
|
||||
}
|
||||
|
||||
KIA_7E4_COUNTER++;
|
||||
if (KIA_7E4_COUNTER > 0x0D) { // gets up to 0x010C before repeating
|
||||
KIA_7E4_COUNTER = 0x01;
|
||||
}
|
||||
}
|
||||
//Send 10s CANFD message
|
||||
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
|
||||
previousMillis10s = currentMillis;
|
||||
|
||||
ok_start_polling_battery = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Kia64FDBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
datalayer.battery.info.number_of_cells = 96;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||
}
|
631
Software/src/battery/KIA-64FD-BATTERY.h
Normal file
631
Software/src/battery/KIA-64FD-BATTERY.h
Normal file
|
@ -0,0 +1,631 @@
|
|||
#ifndef KIA_64_FD_BATTERY_H
|
||||
#define KIA_64_FD_BATTERY_H
|
||||
#include <Arduino.h>
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
|
||||
|
||||
class Kia64FDBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
static constexpr const char* Name = "Kia 64kWh FD battery";
|
||||
|
||||
private:
|
||||
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps);
|
||||
uint16_t estimateSOCFromCell(uint16_t cellVoltage);
|
||||
uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value);
|
||||
uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high);
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4032; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 2400;
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAXCHARGEPOWERALLOWED = 10000;
|
||||
static const int MAXDISCHARGEPOWERALLOWED = 10000;
|
||||
static const int RAMPDOWN_SOC = 9000; // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||
static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing
|
||||
|
||||
// Used for SoC compensation - Define internal resistance value in milliohms for the entire pack
|
||||
// How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance
|
||||
static const int PACK_INTERNAL_RESISTANCE_MOHM = 200; // 200 milliohms for the whole pack
|
||||
|
||||
unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
|
||||
unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
|
||||
|
||||
uint16_t inverterVoltageFrameHigh = 0;
|
||||
uint16_t inverterVoltage = 0;
|
||||
uint16_t soc_calculated = 0;
|
||||
uint16_t SOC_BMS = 0;
|
||||
uint16_t SOC_Display = 0;
|
||||
uint16_t SOC_estimated_lowest = 0;
|
||||
uint16_t SOC_estimated_highest = 0;
|
||||
uint16_t batterySOH = 1000;
|
||||
uint16_t CellVoltMax_mV = 3700;
|
||||
uint16_t CellVoltMin_mV = 3700;
|
||||
uint16_t batteryVoltage = 0;
|
||||
int16_t leadAcidBatteryVoltage = 120;
|
||||
int16_t batteryAmps = 0;
|
||||
int16_t temperatureMax = 0;
|
||||
int16_t temperatureMin = 0;
|
||||
int16_t allowedDischargePower = 0;
|
||||
int16_t allowedChargePower = 0;
|
||||
int16_t poll_data_pid = 0;
|
||||
uint8_t CellVmaxNo = 0;
|
||||
uint8_t CellVminNo = 0;
|
||||
uint8_t batteryManagementMode = 0;
|
||||
uint8_t BMS_ign = 0;
|
||||
bool startedUp = false;
|
||||
bool ok_start_polling_battery = false;
|
||||
uint8_t KIA_7E4_COUNTER = 0x01;
|
||||
int8_t temperature_water_inlet = 0;
|
||||
int8_t heatertemp = 0;
|
||||
unsigned long startMillis = 0;
|
||||
uint8_t messageIndex = 0;
|
||||
|
||||
const unsigned char crc8_table[256] = {
|
||||
// CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7,
|
||||
0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE,
|
||||
0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2,
|
||||
0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC,
|
||||
0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78,
|
||||
0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44,
|
||||
0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52,
|
||||
0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
|
||||
0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73,
|
||||
0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED,
|
||||
0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8,
|
||||
0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95,
|
||||
0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31,
|
||||
0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4};
|
||||
|
||||
// Define the data points for %SOC depending on cell voltage
|
||||
const uint8_t numPoints = 100;
|
||||
|
||||
const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600,
|
||||
8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100,
|
||||
7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600,
|
||||
5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100,
|
||||
4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600,
|
||||
2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100,
|
||||
1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0};
|
||||
|
||||
const uint16_t voltage[101] = {
|
||||
4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941,
|
||||
3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795,
|
||||
3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650,
|
||||
3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504,
|
||||
3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358,
|
||||
3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000};
|
||||
/* These messages are needed for contactor closing */
|
||||
uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40,
|
||||
45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70,
|
||||
70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115,
|
||||
119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150};
|
||||
static constexpr CAN_frame message_1 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_2 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_3 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_4 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_5 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_6 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_7 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
|
||||
static constexpr CAN_frame message_8 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_9 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_10 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_11 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_12 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_13 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_14 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_15 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_16 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_17 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_18 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_19 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_20 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_21 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_22 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2D5,
|
||||
.data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_23 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2EA,
|
||||
.data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_24 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x306,
|
||||
.data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83,
|
||||
0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_25 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x308,
|
||||
.data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_26 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_27 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_28 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_29 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_30 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x33A,
|
||||
.data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_31 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x350,
|
||||
.data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_32 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_33 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_34 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_35 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E5,
|
||||
.data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_36 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x3B5,
|
||||
.data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_37 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_38 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_39 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_40 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_41 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_42 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_43 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_44 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_45 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_46 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_47 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_48 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_49 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
|
||||
static constexpr CAN_frame message_50 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_51 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_52 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_53 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_54 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_55 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_56 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_57 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_58 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_59 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_60 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_61 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2C0,
|
||||
.data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_62 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_63 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
const CAN_frame* messages[64] = {
|
||||
&message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8,
|
||||
&message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16,
|
||||
&message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24,
|
||||
&message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32,
|
||||
&message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40,
|
||||
&message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48,
|
||||
&message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56,
|
||||
&message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63};
|
||||
|
||||
/* PID polling messages */
|
||||
CAN_frame KIA64FD_7E4 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01
|
||||
CAN_frame KIA64FD_ack = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
||||
};
|
||||
|
||||
#endif
|
|
@ -6,45 +6,8 @@
|
|||
#include "../devboard/utils/logging.h"
|
||||
#include "../system_settings.h"
|
||||
|
||||
const unsigned char crc8_table[256] =
|
||||
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
|
||||
0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0,
|
||||
0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23,
|
||||
0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
|
||||
0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B,
|
||||
0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B,
|
||||
0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8,
|
||||
0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
|
||||
0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC,
|
||||
0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B,
|
||||
0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C,
|
||||
0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
|
||||
0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47,
|
||||
0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0,
|
||||
0xE3, 0xFE, 0xD9, 0xC4};
|
||||
|
||||
// Define the data points for %SOC depending on cell voltage
|
||||
const uint8_t numPoints = 100;
|
||||
|
||||
const uint16_t SOC[] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600,
|
||||
8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100,
|
||||
7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600,
|
||||
5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100,
|
||||
4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600,
|
||||
2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100,
|
||||
1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0};
|
||||
|
||||
const uint16_t voltage[] = {4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953,
|
||||
3950, 3941, 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830,
|
||||
3821, 3812, 3804, 3795, 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701,
|
||||
3692, 3684, 3675, 3667, 3658, 3650, 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572,
|
||||
3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, 3495, 3487, 3478, 3470, 3461, 3452, 3444,
|
||||
3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, 3350, 3338, 3325, 3313, 3299,
|
||||
3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000};
|
||||
|
||||
// Function to estimate SOC based on cell voltage
|
||||
uint16_t estimateSOCFromCell(uint16_t cellVoltage) {
|
||||
uint16_t KiaEGmpBattery::estimateSOCFromCell(uint16_t cellVoltage) {
|
||||
if (cellVoltage >= voltage[0]) {
|
||||
return SOC[0];
|
||||
}
|
||||
|
@ -92,26 +55,12 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i
|
|||
// Calculate average cell voltage in millivolts
|
||||
uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Pack: ");
|
||||
logging.print(packVoltage / 10.0);
|
||||
logging.print("V, Current: ");
|
||||
logging.print(currentAmps / 10.0);
|
||||
logging.print("A, Drop: ");
|
||||
logging.print(voltageDrop / 1000.0);
|
||||
logging.print("V, Comp Pack: ");
|
||||
logging.print(compensatedPackVoltageMv / 1000.0);
|
||||
logging.print("V, Avg Cell: ");
|
||||
logging.print(avgCellVoltage);
|
||||
logging.println("mV");
|
||||
#endif
|
||||
|
||||
// Use the cell voltage lookup table to estimate SOC
|
||||
return estimateSOCFromCell(avgCellVoltage);
|
||||
}
|
||||
|
||||
// Fix: Change parameter types to uint16_t to match SOC values
|
||||
uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
||||
uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
||||
if (SOC_low == 0 || SOC_high == 0) {
|
||||
return 0; // If either value is 0, return 0
|
||||
}
|
||||
|
@ -121,536 +70,9 @@ uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
|||
return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value
|
||||
}
|
||||
|
||||
/* These messages are needed for contactor closing */
|
||||
unsigned long startMillis = 0;
|
||||
uint8_t messageIndex = 0;
|
||||
uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40,
|
||||
45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70,
|
||||
70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115,
|
||||
119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150};
|
||||
static constexpr CAN_frame message_1 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_2 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_3 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_4 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_5 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_6 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_7 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
|
||||
static constexpr CAN_frame message_8 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_9 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_10 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_11 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_12 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_13 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_14 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_15 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_16 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_17 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_18 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_19 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_20 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_21 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_22 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2D5,
|
||||
.data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_23 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2EA,
|
||||
.data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_24 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x306,
|
||||
.data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83,
|
||||
0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_25 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x308,
|
||||
.data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_26 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_27 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_28 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_29 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_30 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x33A,
|
||||
.data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_31 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x350,
|
||||
.data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_32 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_33 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_34 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_35 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E5,
|
||||
.data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_36 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x3B5,
|
||||
.data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_37 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_38 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_39 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_40 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_41 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_42 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_43 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_44 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_45 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_46 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_47 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_48 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_49 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
|
||||
static constexpr CAN_frame message_50 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_51 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_52 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_53 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_54 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_55 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_56 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_57 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_58 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_59 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_60 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_61 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2C0,
|
||||
.data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_62 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
static constexpr CAN_frame message_63 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static const CAN_frame* messages[] = {
|
||||
&message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8,
|
||||
&message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16,
|
||||
&message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24,
|
||||
&message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32,
|
||||
&message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40,
|
||||
&message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48,
|
||||
&message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56,
|
||||
&message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63};
|
||||
/* PID polling messages */
|
||||
CAN_frame EGMP_7E4 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01
|
||||
static constexpr CAN_frame EGMP_7E4_ack = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
||||
|
||||
void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
||||
void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if ((rx_frame.data.u8[start + i] * 20) > 1000) {
|
||||
if ((rx_frame.data.u8[start + i] * 20) > 2600) {
|
||||
datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20);
|
||||
}
|
||||
}
|
||||
|
@ -682,7 +104,7 @@ void KiaEGmpBattery::set_voltage_minmax_limits() {
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) {
|
||||
uint8_t KiaEGmpBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) {
|
||||
uint8_t crc = initial_value;
|
||||
for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC
|
||||
crc = crc8_table[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
||||
|
@ -690,19 +112,19 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_
|
|||
return crc;
|
||||
}
|
||||
|
||||
void KiaEGmpBattery::
|
||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
void KiaEGmpBattery::update_values() {
|
||||
|
||||
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
|
||||
// Use the simplified pack-based SOC estimation with proper compensation
|
||||
datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
||||
if (user_selected_use_estimated_SOC) {
|
||||
// Use the simplified pack-based SOC estimation with proper compensation
|
||||
datalayer.battery.status.real_soc =
|
||||
estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
||||
|
||||
// For comparison or fallback, we can still calculate from min/max cell voltages
|
||||
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
||||
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
||||
#else
|
||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||
#endif
|
||||
// For comparison or fallback, we can still calculate from min/max cell voltages
|
||||
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
||||
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
||||
} else {
|
||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||
}
|
||||
|
||||
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
||||
|
||||
|
@ -722,12 +144,12 @@ void KiaEGmpBattery::
|
|||
datalayer.battery.status.max_charge_power_W =
|
||||
RAMPDOWNPOWERALLOWED * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
|
||||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||
datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W;
|
||||
}
|
||||
|
||||
//datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
|
||||
//The allowed discharge power is not available. We hardcode this value for now
|
||||
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
|
||||
datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
|
||||
|
||||
|
@ -749,67 +171,6 @@ void KiaEGmpBattery::
|
|||
if (leadAcidBatteryVoltage < 110) {
|
||||
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
|
||||
}
|
||||
|
||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println(); //sepatator
|
||||
logging.println("Values from battery: ");
|
||||
logging.print("SOC BMS: ");
|
||||
logging.print((uint16_t)SOC_BMS / 10.0, 1);
|
||||
logging.print("% | SOC Display: ");
|
||||
logging.print((uint16_t)SOC_Display / 10.0, 1);
|
||||
logging.print("% | SOH ");
|
||||
logging.print((uint16_t)batterySOH / 10.0, 1);
|
||||
logging.println("%");
|
||||
logging.print((int16_t)batteryAmps / 10.0, 1);
|
||||
logging.print(" Amps | ");
|
||||
logging.print((uint16_t)batteryVoltage / 10.0, 1);
|
||||
logging.print(" Volts | ");
|
||||
logging.print((int16_t)datalayer.battery.status.active_power_W);
|
||||
logging.println(" Watts");
|
||||
logging.print("Allowed Charge ");
|
||||
logging.print((uint16_t)allowedChargePower * 10);
|
||||
logging.print(" W | Allowed Discharge ");
|
||||
logging.print((uint16_t)allowedDischargePower * 10);
|
||||
logging.println(" W");
|
||||
logging.print("MaxCellVolt ");
|
||||
logging.print(CellVoltMax_mV);
|
||||
logging.print(" mV No ");
|
||||
logging.print(CellVmaxNo);
|
||||
logging.print(" | MinCellVolt ");
|
||||
logging.print(CellVoltMin_mV);
|
||||
logging.print(" mV No ");
|
||||
logging.println(CellVminNo);
|
||||
logging.print("TempHi ");
|
||||
logging.print((int16_t)temperatureMax);
|
||||
logging.print("°C TempLo ");
|
||||
logging.print((int16_t)temperatureMin);
|
||||
logging.print("°C WaterInlet ");
|
||||
logging.print((int8_t)temperature_water_inlet);
|
||||
logging.print("°C PowerRelay ");
|
||||
logging.print((int8_t)powerRelayTemperature * 2);
|
||||
logging.println("°C");
|
||||
logging.print("Aux12volt: ");
|
||||
logging.print((int16_t)leadAcidBatteryVoltage / 10.0, 1);
|
||||
logging.println("V | ");
|
||||
logging.print("BmsManagementMode ");
|
||||
logging.print((uint8_t)batteryManagementMode, BIN);
|
||||
if (bitRead((uint8_t)BMS_ign, 2) == 1) {
|
||||
logging.print(" | BmsIgnition ON");
|
||||
} else {
|
||||
logging.print(" | BmsIgnition OFF");
|
||||
}
|
||||
|
||||
if (bitRead((uint8_t)batteryRelay, 0) == 1) {
|
||||
logging.print(" | PowerRelay ON");
|
||||
} else {
|
||||
logging.print(" | PowerRelay OFF");
|
||||
}
|
||||
logging.print(" | Inverter ");
|
||||
logging.print(inverterVoltage);
|
||||
logging.println(" Volts");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Getter implementations for HTML renderer
|
||||
|
@ -890,14 +251,10 @@ void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x7EC:
|
||||
// print_canfd_frame(frame);
|
||||
switch (rx_frame.data.u8[0]) {
|
||||
case 0x10: //"PID Header"
|
||||
// logging.println ("Send ack");
|
||||
poll_data_pid = rx_frame.data.u8[4];
|
||||
// if (rx_frame.data.u8[4] == poll_data_pid) {
|
||||
transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS if the same frame is sent as polled
|
||||
// }
|
||||
transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS
|
||||
break;
|
||||
case 0x21: //First frame in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
#include "CanBattery.h"
|
||||
#include "KIA-E-GMP-HTML.h"
|
||||
|
||||
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
|
||||
|
||||
#ifdef KIA_E_GMP_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS KiaEGmpBattery
|
||||
#endif
|
||||
extern bool user_selected_use_estimated_SOC;
|
||||
|
||||
class KiaEGmpBattery : public CanBattery {
|
||||
public:
|
||||
|
@ -30,6 +26,10 @@ class KiaEGmpBattery : public CanBattery {
|
|||
private:
|
||||
KiaEGMPHtmlRenderer renderer;
|
||||
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps);
|
||||
uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high);
|
||||
uint16_t estimateSOCFromCell(uint16_t cellVoltage);
|
||||
uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value);
|
||||
void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell);
|
||||
void set_voltage_minmax_limits();
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 8064; //5000 = 500.0V
|
||||
|
@ -37,8 +37,6 @@ class KiaEGmpBattery : public CanBattery {
|
|||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAXCHARGEPOWERALLOWED = 10000;
|
||||
static const int MAXDISCHARGEPOWERALLOWED = 10000;
|
||||
static const int RAMPDOWN_SOC = 9000; // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||
static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing
|
||||
|
||||
|
@ -81,9 +79,510 @@ class KiaEGmpBattery : public CanBattery {
|
|||
int8_t powerRelayTemperature = 10;
|
||||
int8_t heatertemp = 20;
|
||||
bool set_voltage_limits = false;
|
||||
uint8_t ticks_200ms_counter = 0;
|
||||
uint8_t EGMP_1CF_counter = 0;
|
||||
uint8_t EGMP_3XF_counter = 0;
|
||||
|
||||
const unsigned char crc8_table[256] = {
|
||||
// CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7,
|
||||
0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE,
|
||||
0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2,
|
||||
0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC,
|
||||
0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78,
|
||||
0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44,
|
||||
0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52,
|
||||
0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
|
||||
0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73,
|
||||
0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED,
|
||||
0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8,
|
||||
0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95,
|
||||
0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31,
|
||||
0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4};
|
||||
// Define the data points for %SOC depending on cell voltage
|
||||
const uint8_t numPoints = 100;
|
||||
|
||||
const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600,
|
||||
8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100,
|
||||
7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600,
|
||||
5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100,
|
||||
4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600,
|
||||
2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100,
|
||||
1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0};
|
||||
|
||||
const uint16_t voltage[101] = {
|
||||
4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941,
|
||||
3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795,
|
||||
3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650,
|
||||
3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504,
|
||||
3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358,
|
||||
3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000};
|
||||
/* These messages are needed for contactor closing */
|
||||
unsigned long startMillis = 0;
|
||||
uint8_t messageIndex = 0;
|
||||
uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40,
|
||||
45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70,
|
||||
70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115,
|
||||
119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150};
|
||||
static constexpr CAN_frame message_1 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_2 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_3 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_4 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_5 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_6 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_7 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
static constexpr CAN_frame message_8 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_9 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_10 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_11 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_12 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_13 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_14 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_15 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_16 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_17 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_18 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_19 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_20 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_21 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_22 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2D5,
|
||||
.data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_23 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2EA,
|
||||
.data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_24 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x306,
|
||||
.data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83,
|
||||
0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_25 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x308,
|
||||
.data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_26 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_27 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_28 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_29 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_30 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x33A,
|
||||
.data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_31 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x350,
|
||||
.data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_32 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_33 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_34 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_35 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E5,
|
||||
.data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_36 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x3B5,
|
||||
.data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_37 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_38 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_39 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_40 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_41 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_42 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_43 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_44 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_45 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_46 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_47 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_48 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_49 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x30A,
|
||||
.data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27,
|
||||
0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}};
|
||||
static constexpr CAN_frame message_50 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x320,
|
||||
.data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03,
|
||||
0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_51 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_52 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_53 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_54 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_55 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_56 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_57 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_58 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x120,
|
||||
.data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_59 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x19A,
|
||||
.data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52,
|
||||
0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_60 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2B5,
|
||||
.data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_61 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2C0,
|
||||
.data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_62 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x2E0,
|
||||
.data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
static constexpr CAN_frame message_63 = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 32,
|
||||
.ID = 0x10A,
|
||||
.data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35,
|
||||
0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
const CAN_frame* messages[63] = {
|
||||
&message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8,
|
||||
&message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16,
|
||||
&message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24,
|
||||
&message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32,
|
||||
&message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40,
|
||||
&message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48,
|
||||
&message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56,
|
||||
&message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63};
|
||||
/* PID polling messages */
|
||||
CAN_frame EGMP_7E4 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01
|
||||
static constexpr CAN_frame EGMP_7E4_ack = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "KIA-HYUNDAI-64-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
#include "CanBattery.h"
|
||||
#include "KIA-HYUNDAI-64-HTML.h"
|
||||
|
||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
|
||||
#endif
|
||||
|
||||
class KiaHyundai64Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
@ -70,7 +66,7 @@ class KiaHyundai64Battery : public CanBattery {
|
|||
uint16_t CellVoltMin_mV = 3700;
|
||||
uint16_t allowedDischargePower = 0;
|
||||
uint16_t allowedChargePower = 0;
|
||||
uint16_t batteryVoltage = 0;
|
||||
uint16_t batteryVoltage = 3700;
|
||||
uint16_t inverterVoltageFrameHigh = 0;
|
||||
uint16_t inverterVoltage = 0;
|
||||
uint16_t cellvoltages_mv[98];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef _KIA_HYUNDAI_64_HTML_H
|
||||
#define _KIA_HYUNDAI_64_HTML_H
|
||||
|
||||
#include <cstring> //For unit test
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -43,7 +44,6 @@ void KiaHyundaiHybridBattery::
|
|||
}
|
||||
|
||||
void KiaHyundaiHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x5F1:
|
||||
break;
|
||||
|
@ -52,6 +52,8 @@ void KiaHyundaiHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 0x588:
|
||||
break;
|
||||
case 0x5AE:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
interlock_missing = (bool)(rx_frame.data.u8[1] & 0x02) >> 1;
|
||||
break;
|
||||
case 0x5AF:
|
||||
|
@ -199,15 +201,21 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
poll_data_pid++;
|
||||
if (poll_data_pid == 1) {
|
||||
transmit_can_frame(&KIA_7E4_id1);
|
||||
KIA_7E4.data.u8[2] = 0x01;
|
||||
KIA_7E4.data.u8[3] = 0x00;
|
||||
transmit_can_frame(&KIA_7E4);
|
||||
} else if (poll_data_pid == 2) {
|
||||
transmit_can_frame(&KIA_7E4_id2);
|
||||
KIA_7E4.data.u8[2] = 0x02;
|
||||
transmit_can_frame(&KIA_7E4);
|
||||
} else if (poll_data_pid == 3) {
|
||||
transmit_can_frame(&KIA_7E4_id3);
|
||||
KIA_7E4.data.u8[2] = 0x03;
|
||||
transmit_can_frame(&KIA_7E4);
|
||||
} else if (poll_data_pid == 4) {
|
||||
|
||||
//Group 4 not polled
|
||||
} else if (poll_data_pid == 5) {
|
||||
transmit_can_frame(&KIA_7E4_id5);
|
||||
KIA_7E4.data.u8[2] = 0x05;
|
||||
KIA_7E4.data.u8[3] = 0x04;
|
||||
transmit_can_frame(&KIA_7E4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#define KIA_HYUNDAI_HYBRID_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery
|
||||
#endif
|
||||
|
||||
class KiaHyundaiHybridBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -38,26 +34,11 @@ class KiaHyundaiHybridBattery : public CanBattery {
|
|||
uint16_t min_cell_voltage_mv = 3700;
|
||||
uint16_t max_cell_voltage_mv = 3700;
|
||||
|
||||
CAN_frame KIA_7E4_id1 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame KIA_7E4_id2 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame KIA_7E4_id3 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame KIA_7E4_id5 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame KIA_7E4 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x7E4,
|
||||
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame KIA_7E4_ack = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "MEB-BATTERY.h"
|
||||
#include <Arduino.h>
|
||||
#include <algorithm> // For std::min and std::max
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../communication/can/obd.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
|
@ -171,9 +172,7 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
|
|||
magicByte = MB16A954A6[counter];
|
||||
break;
|
||||
default: // this won't lead to correct CRC checksums
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Checksum request unknown");
|
||||
#endif
|
||||
magicByte = 0x00;
|
||||
break;
|
||||
}
|
||||
|
@ -202,15 +201,15 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
|
|||
void MebBattery::
|
||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
|
||||
datalayer_battery->status.real_soc = battery_SOC * 5; //*0.05*100
|
||||
|
||||
datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
|
||||
datalayer_battery->status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
|
||||
|
||||
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||
datalayer_battery->status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||
|
||||
if (nof_cells_determined) {
|
||||
datalayer.battery.info.total_capacity_Wh =
|
||||
((float)datalayer.battery.info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564;
|
||||
datalayer_battery->info.total_capacity_Wh =
|
||||
((float)datalayer_battery->info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564;
|
||||
// The factor 1.02564 = 1/0.975 is to correct for bottom 2.5% which is reported by the remaining_capacity_Wh,
|
||||
// but which is not actually usable, but if we do not include it, the remaining_capacity_Wh can be larger than
|
||||
// the total_capacity_Wh.
|
||||
|
@ -218,32 +217,32 @@ void MebBattery::
|
|||
// total_capacity_Wh calculated above.
|
||||
|
||||
int Wh_max = 61832 * 0.935; // 108 cells
|
||||
if (datalayer.battery.info.number_of_cells <= 84)
|
||||
if (datalayer_battery->info.number_of_cells <= 84)
|
||||
Wh_max = 48091 * 0.9025;
|
||||
else if (datalayer.battery.info.number_of_cells <= 96)
|
||||
else if (datalayer_battery->info.number_of_cells <= 96)
|
||||
Wh_max = 82442 * 0.9025;
|
||||
if (BMS_capacity_ah > 0)
|
||||
datalayer.battery.status.soh_pptt = 10000 * datalayer.battery.info.total_capacity_Wh / (Wh_max * 1.02564);
|
||||
datalayer_battery->status.soh_pptt = 10000 * datalayer_battery->info.total_capacity_Wh / (Wh_max * 1.02564);
|
||||
}
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||
datalayer_battery->status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||
datalayer_battery->status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = (max_discharge_power_watt * 100);
|
||||
datalayer_battery->status.max_discharge_power_W = (max_discharge_power_watt * 100);
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery.status.active_power_W =
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
datalayer_battery->status.active_power_W =
|
||||
((datalayer_battery->status.voltage_dV * datalayer_battery->status.current_dA) / 100);
|
||||
|
||||
// datalayer.battery.status.temperature_min_dC = actual_temperature_lowest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||
datalayer_battery->status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||
|
||||
// datalayer.battery.status.temperature_max_dC = actual_temperature_highest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||
datalayer_battery->status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||
|
||||
//Map all cell voltages to the global array
|
||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||
memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||
|
||||
if (service_disconnect_switch_missing) {
|
||||
set_event(EVENT_HVIL_FAILURE, 1);
|
||||
|
@ -310,9 +309,7 @@ void MebBattery::
|
|||
void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
last_can_msg_timestamp = millis();
|
||||
if (first_can_msg == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MEB: First CAN msg received\n");
|
||||
#endif
|
||||
first_can_msg = last_can_msg_timestamp;
|
||||
}
|
||||
|
||||
|
@ -326,9 +323,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
if (rx_frame.data.u8[0] !=
|
||||
vw_crc_calc(rx_frame.data.u8, rx_frame.DLC, rx_frame.ID)) { //If CRC does not match calc
|
||||
datalayer.battery.status.CAN_error_counter++;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MEB: Msg 0x%04X CRC error\n", rx_frame.ID);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
@ -337,6 +332,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
|
||||
switch (rx_frame.ID) {
|
||||
case 0x17F0007B: // BMS 500ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x17F0007B;
|
||||
component_protection_active = (rx_frame.data.u8[0] & 0x01);
|
||||
shutdown_active = ((rx_frame.data.u8[0] & 0x02) >> 1);
|
||||
|
@ -352,11 +348,13 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 0x17FE007B: // BMS - Offboard tester diag response
|
||||
break;
|
||||
case 0x1B00007B: // BMS - 200ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
wakeup_type =
|
||||
((rx_frame.data.u8[1] & 0x10) >> 4); //0 passive, SG has not woken up, 1 active, SG has woken up the network
|
||||
instrumentation_cluster_request = ((rx_frame.data.u8[1] & 0x40) >> 6); //True/false
|
||||
break;
|
||||
case 0x12DD54D0: // BMS Limits 100ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x12DD54D0;
|
||||
max_discharge_power_watt =
|
||||
((rx_frame.data.u8[6] & 0x07) << 10) | (rx_frame.data.u8[5] << 2) | (rx_frame.data.u8[4] & 0xC0) >> 6; //*100
|
||||
|
@ -366,6 +364,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
max_charge_current_amp = ((rx_frame.data.u8[4] & 0x3F) << 7) | (rx_frame.data.u8[3] >> 1); //*0.2
|
||||
break;
|
||||
case 0x12DD54D1: // BMS 100ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x12DD54D1;
|
||||
if (rx_frame.data.u8[6] != 0xFE || rx_frame.data.u8[7] != 0xFF) { // Init state, values below invalid
|
||||
battery_SOC = ((rx_frame.data.u8[3] & 0x0F) << 7) | (rx_frame.data.u8[2] >> 1); //*0.05
|
||||
|
@ -377,6 +376,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
warning_support = (rx_frame.data.u8[1] & 0x70) >> 4;
|
||||
break;
|
||||
case 0x12DD54D2: // BMS 100ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x12DD54D2;
|
||||
battery_heating_active = (rx_frame.data.u8[4] & 0x40) >> 6;
|
||||
heating_request = (rx_frame.data.u8[5] & 0xE0) >> 5;
|
||||
|
@ -385,6 +385,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
power_battery_heating_req_watt = rx_frame.data.u8[7];
|
||||
break;
|
||||
case 0x1A555550: // BMS 500ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x1A555550;
|
||||
balancing_active = (rx_frame.data.u8[1] & 0xC0) >> 6;
|
||||
charging_active = (rx_frame.data.u8[2] & 0x01);
|
||||
|
@ -394,6 +395,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
isolation_resistance_kOhm = (((rx_frame.data.u8[3] & 0x1F) << 7) | rx_frame.data.u8[2] >> 1); //*5
|
||||
break;
|
||||
case 0x1A555551: // BMS 500ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x1A555551;
|
||||
battery_heating_installed = (rx_frame.data.u8[1] & 0x20) >> 5;
|
||||
error_NT_circuit = (rx_frame.data.u8[1] & 0x40) >> 6;
|
||||
|
@ -407,6 +409,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
|
||||
break;
|
||||
case 0x1A5555B2: // BMS
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x1A5555B2;
|
||||
performance_index_discharge_peak_temperature_percentage =
|
||||
(((rx_frame.data.u8[3] & 0x07) << 6) | rx_frame.data.u8[2] >> 2); //*0.2
|
||||
|
@ -416,6 +419,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
temperature_status_charge = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
|
||||
break;
|
||||
case 0x16A954A6: // BMS
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
can_msg_received |= RX_0x16A954A6;
|
||||
BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
||||
isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5;
|
||||
|
@ -429,11 +433,13 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
datalayer.battery.status.cell_max_voltage_mV = actual_cellvoltage_highest_mV + 1000;
|
||||
}
|
||||
break;
|
||||
case 0x16A954F8: // BMS
|
||||
case 0x16A954F8: // BMS
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
predicted_power_dyn_standard_watt = ((rx_frame.data.u8[6] << 1) | rx_frame.data.u8[5] >> 7); //*50
|
||||
predicted_time_dyn_standard_minutes = rx_frame.data.u8[7];
|
||||
break;
|
||||
case 0x16A954E8: // BMS Temperature and cellvoltages - 180ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
mux = (rx_frame.data.u8[0] & 0x0F);
|
||||
switch (mux) {
|
||||
case 0: // Temperatures 1-56. Value is 0xFD if sensor not present
|
||||
|
@ -700,29 +706,23 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 3: // EXTERN CHARGING
|
||||
case 4: // AC_CHARGING
|
||||
case 6: // DC_CHARGING
|
||||
#ifdef DEBUG_LOG
|
||||
if (!datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors closed\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_ACTIVE;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 5: // Error
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
datalayer.battery.status.real_bms_status = BMS_FAULT;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 7: // Init
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
|
@ -730,10 +730,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 2: // BALANCING
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
|
@ -1273,16 +1271,11 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
}
|
||||
break;
|
||||
case 0x18DAF105:
|
||||
handle_obd_frame(rx_frame);
|
||||
handle_obd_frame(rx_frame, can_interface);
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Unknown CAN frame received:\n");
|
||||
dump_can_frame(rx_frame, MSG_RX);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
if (can_msg_received == 0xFFFF && nof_cells_determined) {
|
||||
if (datalayer.battery.status.real_bms_status == BMS_DISCONNECTED)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
|
@ -1292,10 +1285,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
void MebBattery::transmit_can(unsigned long currentMillis) {
|
||||
|
||||
if (currentMillis - last_can_msg_timestamp > 500) {
|
||||
#ifdef DEBUG_LOG
|
||||
if (first_can_msg)
|
||||
logging.printf("MEB: No CAN msg received for 500ms\n");
|
||||
#endif
|
||||
can_msg_received = RX_DEFAULT;
|
||||
first_can_msg = 0;
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT) {
|
||||
|
@ -1373,7 +1364,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) {
|
||||
hv_requested = true;
|
||||
datalayer.system.settings.start_precharging = false;
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB: Requesting HV\n");
|
||||
}
|
||||
|
@ -1385,7 +1375,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x30 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING;
|
||||
|
@ -1399,7 +1388,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
datalayer.system.settings.start_precharging = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB: Requesting HV_OFF\n");
|
||||
}
|
||||
|
@ -1411,7 +1399,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x10 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
|
||||
|
|
|
@ -3,12 +3,21 @@
|
|||
#include "CanBattery.h"
|
||||
#include "MEB-HTML.h"
|
||||
|
||||
#ifdef MEB_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS MebBattery
|
||||
#endif
|
||||
|
||||
class MebBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
MebBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_MEB* extended, CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
|
||||
BMS_voltage = 0;
|
||||
}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
MebBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
datalayer_meb = &datalayer_extended.meb;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -21,6 +30,10 @@ class MebBattery : public CanBattery {
|
|||
|
||||
private:
|
||||
MebHtmlRenderer renderer;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_MEB* datalayer_meb;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
|
||||
static const int MAX_PACK_VOLTAGE_96S_DV = 4032;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "MG-5-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -33,7 +34,6 @@ void Mg5Battery::
|
|||
}
|
||||
|
||||
void Mg5Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x171: //Following messages were detected on a MG5 battery BMS
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN
|
||||
|
@ -112,7 +112,7 @@ void Mg5Battery::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
|
||||
void Mg5Battery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63);
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#define MG_5_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef MG_5_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS Mg5Battery
|
||||
#endif
|
||||
|
||||
class Mg5Battery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "MG-HS-PHEV-BATTERY.h"
|
||||
#include <cmath> //For unit test
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../communication/contactorcontrol/comm_contactorcontrol.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
|
@ -14,8 +16,6 @@ changing.
|
|||
|
||||
OPTIONAL SETTINGS
|
||||
|
||||
Put these in your USER_SETTINGS.h:
|
||||
|
||||
// This will scale the SoC so the batteries top out at 4.2V/cell instead of
|
||||
4.1V/cell. The car only seems to use up to 4.1V/cell in service.
|
||||
#define MG_HS_PHEV_USE_FULL_CAPACITY true
|
||||
|
@ -147,11 +147,9 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
// 15 = isolation fault
|
||||
// 0/8 = checking
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (rx_frame.data.u8[1] != previousState) {
|
||||
logging.printf("MG_HS_PHEV: Battery status changed to %d (%d)\n", rx_frame.data.u8[1], rx_frame.data.u8[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rx_frame.data.u8[1] == 0xf && previousState != 0xf) {
|
||||
// Isolation fault, set event
|
||||
|
@ -168,18 +166,14 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
// A weird 'stuck' state where the battery won't reconnect
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
if (!datalayer.system.status.BMS_startup_in_progress) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MG_HS_PHEV: Stuck, resetting.\n");
|
||||
#endif
|
||||
start_bms_reset();
|
||||
}
|
||||
} else if (rx_frame.data.u8[1] == 0xf) {
|
||||
// A fault state (likely isolation failure)
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
if (!datalayer.system.status.BMS_startup_in_progress) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MG_HS_PHEV: Fault, resetting.\n");
|
||||
#endif
|
||||
start_bms_reset();
|
||||
}
|
||||
} else {
|
||||
|
@ -378,6 +372,5 @@ void MgHsPHEVBattery::setup(void) { // Performs one time setup at startup
|
|||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
|
||||
datalayer.battery.info.number_of_cells = 90;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef MG_HS_PHEV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS MgHsPHEVBattery
|
||||
#endif
|
||||
|
||||
class MgHsPHEVBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#include "NISSAN-LEAF-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../charger/CHARGERS.h"
|
||||
#include "../charger/CanCharger.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "../devboard/utils/logging.h"
|
||||
|
||||
#include "../charger/CHARGERS.h"
|
||||
#include "../charger/CanCharger.h"
|
||||
|
||||
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
|
||||
//Cryptographic functions
|
||||
void decodeChallengeData(unsigned int SeedInput, unsigned char* Crypt_Output_Buffer);
|
||||
|
@ -150,13 +150,14 @@ void NissanLeafBattery::
|
|||
clear_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ);
|
||||
}
|
||||
|
||||
#ifdef INTERLOCK_REQUIRED
|
||||
if (!battery_Interlock) {
|
||||
set_event(EVENT_HVIL_FAILURE, 0);
|
||||
} else {
|
||||
clear_event(EVENT_HVIL_FAILURE);
|
||||
if (user_selected_LEAF_interlock_mandatory) {
|
||||
//If user requires both large 80kW and small 6kW interlock to be seated for operation
|
||||
if (!battery_Interlock) {
|
||||
set_event(EVENT_HVIL_FAILURE, 0);
|
||||
} else {
|
||||
clear_event(EVENT_HVIL_FAILURE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (battery_HeatExist) {
|
||||
if (battery_Heating_Stop) {
|
||||
|
@ -188,6 +189,10 @@ void NissanLeafBattery::
|
|||
datalayer_nissan->HeatingStart = battery_Heating_Start;
|
||||
datalayer_nissan->HeaterSendRequest = battery_Batt_Heater_Mail_Send_Request;
|
||||
datalayer_nissan->battery_HX = battery_HX;
|
||||
datalayer_nissan->temperature1 = ((Temp_fromRAW_to_F(battery_temp_raw_1) - 320) * 5) / 9; //Convert from F to C
|
||||
datalayer_nissan->temperature2 = ((Temp_fromRAW_to_F(battery_temp_raw_2) - 320) * 5) / 9; //Convert from F to C
|
||||
datalayer_nissan->temperature3 = ((Temp_fromRAW_to_F(battery_temp_raw_3) - 320) * 5) / 9; //Convert from F to C
|
||||
datalayer_nissan->temperature4 = ((Temp_fromRAW_to_F(battery_temp_raw_4) - 320) * 5) / 9; //Convert from F to C
|
||||
datalayer_nissan->CryptoChallenge = incomingChallenge;
|
||||
datalayer_nissan->SolvedChallengeMSB =
|
||||
((solvedChallenge[7] << 24) | (solvedChallenge[6] << 16) | (solvedChallenge[5] << 8) | solvedChallenge[4]);
|
||||
|
@ -799,6 +804,8 @@ bool NissanLeafBattery::is_message_corrupt(CAN_frame rx_frame) {
|
|||
uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrible, but apparently works well
|
||||
if (temperature == 1021) {
|
||||
return 10;
|
||||
} else if (temperature == 65535) { //Value unavailable, sensor does not exist
|
||||
return 718; //0*C final calculation
|
||||
} else if (temperature >= 589) {
|
||||
return static_cast<uint16_t>(1620 - temperature * 1.81);
|
||||
} else if (temperature >= 569) {
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
#include "CanBattery.h"
|
||||
#include "NISSAN-LEAF-HTML.h"
|
||||
|
||||
#ifdef NISSAN_LEAF_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS NissanLeafBattery
|
||||
#endif
|
||||
extern bool user_selected_LEAF_interlock_mandatory;
|
||||
|
||||
class NissanLeafBattery : public CanBattery {
|
||||
public:
|
||||
|
@ -198,11 +196,11 @@ class NissanLeafBattery : public CanBattery {
|
|||
uint16_t battery_min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
|
||||
uint16_t battery_HX = 0; //Internal resistance
|
||||
uint16_t battery_insulation = 0; //Insulation resistance
|
||||
uint16_t battery_temp_raw_1 = 0;
|
||||
uint16_t battery_temp_raw_1 = 718;
|
||||
uint8_t battery_temp_raw_2_highnibble = 0;
|
||||
uint16_t battery_temp_raw_2 = 0;
|
||||
uint16_t battery_temp_raw_3 = 0;
|
||||
uint16_t battery_temp_raw_4 = 0;
|
||||
uint16_t battery_temp_raw_2 = 718;
|
||||
uint16_t battery_temp_raw_3 = 718; //This measurement not available on 2013+
|
||||
uint16_t battery_temp_raw_4 = 718;
|
||||
uint16_t battery_temp_raw_max = 0;
|
||||
uint16_t battery_temp_raw_min = 0;
|
||||
int16_t battery_temp_polled_max = 0;
|
||||
|
|
|
@ -54,6 +54,12 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer {
|
|||
content += "<h4>Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "</h4>";
|
||||
content += "<h4>Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "</h4>";
|
||||
content += "<h4>Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "</h4>";
|
||||
content += "<h4>Temperature 1: " + String(datalayer_extended.nissanleaf.temperature1 / 10.0) + " °C</h4>";
|
||||
content += "<h4>Temperature 2: " + String(datalayer_extended.nissanleaf.temperature2 / 10.0) + " °C</h4>";
|
||||
if (datalayer_extended.nissanleaf.LEAF_gen == 0) {
|
||||
content += "<h4>Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3 / 10.0) + " °C</h4>";
|
||||
}
|
||||
content += "<h4>Temperature 4: " + String(datalayer_extended.nissanleaf.temperature4 / 10.0) + " °C</h4>";
|
||||
content += "<h4>CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "</h4>";
|
||||
content += "<h4>SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) +
|
||||
String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "</h4>";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "ORION-BMS.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -57,8 +58,8 @@ void OrionBms::update_values() {
|
|||
|
||||
datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage;
|
||||
|
||||
//If user did not configure amount of cells correctly in the header file, update the value
|
||||
if ((amount_of_detected_cells > NUMBER_OF_CELLS) && (amount_of_detected_cells < MAX_AMOUNT_CELLS)) {
|
||||
//Use the reported number of cells to avoid needing to configure it
|
||||
if (amount_of_detected_cells < MAX_AMOUNT_CELLS) {
|
||||
datalayer.battery.info.number_of_cells = amount_of_detected_cells;
|
||||
}
|
||||
}
|
||||
|
@ -115,10 +116,9 @@ void OrionBms::transmit_can(unsigned long currentMillis) {
|
|||
void OrionBms::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "../system_settings.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef ORION_BMS
|
||||
#define SELECTED_BATTERY_CLASS OrionBms
|
||||
#endif
|
||||
|
||||
class OrionBms : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -17,14 +13,6 @@ class OrionBms : public CanBattery {
|
|||
static constexpr const char* Name = "DIY battery with Orion BMS (Victron setting)";
|
||||
|
||||
private:
|
||||
/* Change the following to suit your battery */
|
||||
static const int NUMBER_OF_CELLS = 96;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
|
||||
uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages
|
||||
uint16_t Maximum_Cell_Voltage = 3700;
|
||||
uint16_t Minimum_Cell_Voltage = 3700;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "PYLON-BATTERY.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -36,7 +37,6 @@ void PylonBattery::update_values() {
|
|||
}
|
||||
|
||||
void PylonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x7310:
|
||||
case 0x7311:
|
||||
|
@ -54,6 +54,7 @@ void PylonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x4210:
|
||||
case 0x4211:
|
||||
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
voltage_dV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||
current_dA = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 30000;
|
||||
SOC = rx_frame.data.u8[6];
|
||||
|
@ -131,10 +132,10 @@ void PylonBattery::setup(void) { // Performs one time setup at startup
|
|||
strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer_battery->info.number_of_cells = 2;
|
||||
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer_battery->info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer_battery->info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer_battery->info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer_battery->info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
|
||||
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef PYLON_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS PylonBattery
|
||||
#endif
|
||||
|
||||
class PylonBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
@ -32,11 +28,6 @@ class PylonBattery : public CanBattery {
|
|||
static constexpr const char* Name = "Pylon compatible battery";
|
||||
|
||||
private:
|
||||
/* Change the following to suit your battery */
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "RANGE-ROVER-PHEV-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -76,7 +77,6 @@ void RangeRoverPhevBattery::update_values() {
|
|||
}
|
||||
|
||||
void RangeRoverPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x080: // 15ms
|
||||
StatusCAT5BPOChg = (rx_frame.data.u8[0] & 0x01);
|
||||
|
@ -104,6 +104,7 @@ void RangeRoverPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
DischargeContPwrLmt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
break;
|
||||
case 0x102: // 20ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
PwrGpCS = rx_frame.data.u8[0];
|
||||
PwrGpCounter = (rx_frame.data.u8[1] & 0x3C) >> 2;
|
||||
VoltageExt = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[2]);
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef RANGE_ROVER_PHEV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RangeRoverPhevBattery
|
||||
#endif
|
||||
|
||||
class RangeRoverPhevBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
155
Software/src/battery/RELION-LV-BATTERY.cpp
Normal file
155
Software/src/battery/RELION-LV-BATTERY.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "RELION-LV-BATTERY.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
/*CAN Type:CAN2.0(Extended)
|
||||
BPS:250kbps
|
||||
Data Length: 8
|
||||
Data Encoded Format:Motorola*/
|
||||
|
||||
void RelionBattery::update_values() {
|
||||
|
||||
datalayer.battery.status.real_soc = battery_soc * 100;
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
||||
|
||||
datalayer.battery.status.soh_pptt = battery_soh * 100;
|
||||
|
||||
datalayer.battery.status.voltage_dV = battery_total_voltage;
|
||||
|
||||
datalayer.battery.status.current_dA = battery_total_current; //Charging negative, discharge positive
|
||||
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
((battery_total_voltage / 10) * charge_current_A); //90A recommended charge current
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
((battery_total_voltage / 10) * discharge_current_A); //150A max continous discharge current
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = max_cell_temperature * 10;
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = max_cell_temperature * 10;
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage;
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage;
|
||||
}
|
||||
|
||||
void RelionBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x02018100: //ID1 (Example frame 10 08 01 F0 00 00 00 00)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_total_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
break;
|
||||
case 0x02028100: //ID2 (Example frame 00 00 00 63 64 10 00 00)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_total_current = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
system_state = rx_frame.data.u8[2];
|
||||
battery_soc = rx_frame.data.u8[3];
|
||||
battery_soh = rx_frame.data.u8[4];
|
||||
most_serious_fault = rx_frame.data.u8[5];
|
||||
break;
|
||||
case 0x02038100: //ID3 (Example frame 0C F9 01 04 0C A7 01 0F)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
max_cell_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
min_cell_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
break;
|
||||
case 0x02648100: //Charging limitis
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
charge_current_A = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) - 800;
|
||||
regen_charge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800;
|
||||
discharge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800;
|
||||
break;
|
||||
case 0x02048100: ///Temperatures min/max 2048100 [8] 47 01 01 47 01 01 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
max_cell_temperature = rx_frame.data.u8[0] - 50;
|
||||
min_cell_temperature = rx_frame.data.u8[2] - 50;
|
||||
break;
|
||||
case 0x02468100: ///Raw temperatures 2468100 [8] 47 47 47 47 47 47 47 47
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02478100: ///? 2478100 [8] 32 32 32 32 32 32 32 32
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
//ID6 = 0x02108100 ~ 0x023F8100****** Cell Voltage 1~192******
|
||||
case 0x02108100: ///Cellvoltages 1 2108100 [8] 0C F9 0C F8 0C F8 0C F9
|
||||
datalayer.battery.status.cell_voltages_mV[0] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[2] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[3] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02118100: ///Cellvoltages 2 2118100 [8] 0C F8 0C F8 0C F9 0C F8
|
||||
datalayer.battery.status.cell_voltages_mV[4] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[5] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[6] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[7] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02128100: ///Cellvoltages 3 2128100 [8] 0C F8 0C F8 0C F9 0C F8
|
||||
datalayer.battery.status.cell_voltages_mV[8] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[9] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[10] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[11] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02138100: ///Cellvoltages 4 2138100 [8] 0C F9 0C CD 0C A7 00 00
|
||||
datalayer.battery.status.cell_voltages_mV[12] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[13] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[14] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02058100: ///? 2058100 [8] 00 0C 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02068100: ///? 2068100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02148100: ///? 2148100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02508100: ///? 2508100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02518100: ///? 2518100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02528100: ///? 2528100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02548100: ///? 2548100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x024A8100: ///? 24A8100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02558100: ///? 2558100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02538100: ///? 2538100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02568100: ///? 2568100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RelionBattery::transmit_can(unsigned long currentMillis) {
|
||||
// No periodic sending for this protocol
|
||||
}
|
||||
|
||||
void RelionBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.chemistry = LFP;
|
||||
datalayer.battery.info.number_of_cells = 16;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
39
Software/src/battery/RELION-LV-BATTERY.h
Normal file
39
Software/src/battery/RELION-LV-BATTERY.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef RELION_BATTERY_H
|
||||
#define RELION_BATTERY_H
|
||||
|
||||
#include "../system_settings.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
class RelionBattery : public CanBattery {
|
||||
public:
|
||||
RelionBattery() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
static constexpr const char* Name = "Relion LV protocol via 250kbps CAN";
|
||||
|
||||
private:
|
||||
static const int MAX_PACK_VOLTAGE_DV = 584; //58.4V recommended charge voltage. BMS protection steps in at 60.8V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 440; //44.0V Recommended LV disconnect. BMS protection steps in at 40.0V
|
||||
static const int MAX_CELL_DEVIATION_MV = 300;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 3800; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
uint16_t battery_total_voltage = 500;
|
||||
int16_t battery_total_current = 0;
|
||||
uint8_t system_state = 0;
|
||||
uint8_t battery_soc = 50;
|
||||
uint8_t battery_soh = 99;
|
||||
uint8_t most_serious_fault = 0;
|
||||
uint16_t max_cell_voltage = 3300;
|
||||
uint16_t min_cell_voltage = 3300;
|
||||
int16_t max_cell_temperature = 0;
|
||||
int16_t min_cell_temperature = 0;
|
||||
int16_t charge_current_A = 0;
|
||||
int16_t regen_charge_current_A = 0;
|
||||
int16_t discharge_current_A = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -51,39 +51,6 @@ void RenaultKangooBattery::
|
|||
datalayer.battery.status.cell_min_voltage_mV = LB_Cell_Min_Voltage;
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Values going to inverter:");
|
||||
logging.print("SOH%: ");
|
||||
logging.print(datalayer.battery.status.soh_pptt);
|
||||
logging.print(", SOC% scaled: ");
|
||||
logging.print(datalayer.battery.status.reported_soc);
|
||||
logging.print(", Voltage: ");
|
||||
logging.print(datalayer.battery.status.voltage_dV);
|
||||
logging.print(", Max discharge power: ");
|
||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
||||
logging.print(", Max charge power: ");
|
||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
||||
logging.print(", Max temp: ");
|
||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
||||
logging.print(", Min temp: ");
|
||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
||||
logging.print(", BMS Status (3=OK): ");
|
||||
logging.print(datalayer.battery.status.bms_status);
|
||||
|
||||
logging.println("Battery values: ");
|
||||
logging.print("Real SOC: ");
|
||||
logging.print(LB_SOC);
|
||||
logging.print(", Current: ");
|
||||
logging.print(LB_Current);
|
||||
logging.print(", kWh remain: ");
|
||||
logging.print(LB_kWh_Remaining);
|
||||
logging.print(", max mV: ");
|
||||
logging.print(LB_Cell_Max_Voltage);
|
||||
logging.print(", min mV: ");
|
||||
logging.print(LB_Cell_Min_Voltage);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenaultKangooBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef RENAULT_KANGOO_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RenaultKangooBattery
|
||||
#endif
|
||||
|
||||
class RenaultKangooBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "RENAULT-TWIZY.h"
|
||||
#include <cstdint>
|
||||
#include <cstring> //For unit test
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
|
@ -51,9 +52,10 @@ void RenaultTwizyBattery::update_values() {
|
|||
}
|
||||
|
||||
void RenaultTwizyBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x155:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
// max charge power is in steps of 300W from 0 to 7
|
||||
max_charge_power = (uint16_t)rx_frame.data.u8[0] * 300;
|
||||
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#define RENAULT_TWIZY_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef RENAULT_TWIZY_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RenaultTwizyBattery
|
||||
#endif
|
||||
|
||||
class RenaultTwizyBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
#include "RENAULT-ZOE-GEN1-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||
|
||||
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
|
||||
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe/src/vehicle_renaultzoe.cpp
|
||||
The Zoe BMS apparently does not send total pack voltage, so we use the polled 96x cellvoltages summed up as total voltage
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "CanBattery.h"
|
||||
#include "RENAULT-ZOE-GEN1-HTML.h"
|
||||
|
||||
#ifdef RENAULT_ZOE_GEN1_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
|
||||
#endif
|
||||
|
||||
class RenaultZoeGen1Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
@ -38,10 +34,10 @@ class RenaultZoeGen1Battery : public CanBattery {
|
|||
private:
|
||||
RenaultZoeGen1HtmlRenderer renderer;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4220; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
|
|
|
@ -403,7 +403,7 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_balancing_shunts[94] = (rx_frame.data.u8[1] & 0x02) >> 1;
|
||||
battery_balancing_shunts[95] = (rx_frame.data.u8[1] & 0x01);
|
||||
|
||||
memcpy(datalayer.battery.status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||
memcpy(datalayer_battery->status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||
}
|
||||
break;
|
||||
case POLL_ENERGY_COMPLETE:
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "CanBattery.h"
|
||||
#include "RENAULT-ZOE-GEN2-HTML.h"
|
||||
|
||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
|
||||
#endif
|
||||
|
||||
class RenaultZoeGen2Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
|
159
Software/src/battery/RIVIAN-BATTERY.cpp
Normal file
159
Software/src/battery/RIVIAN-BATTERY.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include "RIVIAN-BATTERY.h"
|
||||
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
/*
|
||||
Initial support for Rivian BIG battery (135kWh)
|
||||
The battery has 3x CAN channels
|
||||
- Failover CAN (CAN-FD) lots of content, not required for operation. Has all cellvoltages!
|
||||
- Platform CAN (500kbps) with all the control messages needed to control the battery <- This is the one we want
|
||||
- Battery CAN (500kbps) lots of content, not required for operation
|
||||
*/
|
||||
|
||||
void RivianBattery::update_values() {
|
||||
|
||||
datalayer.battery.status.real_soc = battery_SOC;
|
||||
|
||||
datalayer.battery.status.soh_pptt;
|
||||
|
||||
datalayer.battery.status.voltage_dV = battery_voltage;
|
||||
datalayer.battery.status.current_dA = ((int16_t)battery_current / 10.0 - 3200) * 10;
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = ((battery_voltage / 10) * battery_charge_limit_amp);
|
||||
datalayer.battery.status.max_discharge_power_W = ((battery_voltage / 10) * battery_discharge_limit_amp);
|
||||
|
||||
//datalayer.battery.status.cell_min_voltage_mV = 3700; //TODO: Take from failover CAN?
|
||||
//datalayer.battery.status.cell_max_voltage_mV = 3700; //TODO: Take from failover CAN?
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = battery_min_temperature * 10;
|
||||
datalayer.battery.status.temperature_max_dC = battery_max_temperature * 10;
|
||||
}
|
||||
|
||||
void RivianBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x160: //Current [Platform CAN]+
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_current = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||
break;
|
||||
case 0x151: //Celltemps (requires other CAN channel)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x120: //Voltages [Platform CAN]+
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_voltage = (((rx_frame.data.u8[7] & 0x1F) << 8) | rx_frame.data.u8[6]);
|
||||
break;
|
||||
case 0x25A: //SOC and kWh [Platform CAN]+
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//battery_SOC = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]);
|
||||
kWh_available_max =
|
||||
(((rx_frame.data.u8[3] & 0x03) << 14) | (rx_frame.data.u8[2] << 6) | (rx_frame.data.u8[1] >> 2)) / 200;
|
||||
kWh_available_total =
|
||||
(((rx_frame.data.u8[5] & 0x03) << 14) | (rx_frame.data.u8[4] << 6) | (rx_frame.data.u8[3] >> 2)) / 200;
|
||||
break;
|
||||
case 0x405: //State [Platform CAN]+
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
BMS_state = (rx_frame.data.u8[0] & 0x03);
|
||||
break;
|
||||
case 0x100: //Discharge/Charge speed
|
||||
battery_charge_limit_amp =
|
||||
(((rx_frame.data.u8[3] & 0x0F) << 8) | (rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4)) / 20;
|
||||
battery_discharge_limit_amp =
|
||||
(((rx_frame.data.u8[5] & 0x0F) << 8) | (rx_frame.data.u8[4] << 4) | (rx_frame.data.u8[3] >> 4)) / 20;
|
||||
break;
|
||||
case 0x153: //Temperatures
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_max_temperature = (rx_frame.data.u8[5] / 2) - 40;
|
||||
battery_min_temperature = (rx_frame.data.u8[6] / 2) - 40;
|
||||
break;
|
||||
case 0x55B: //Temperatures
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_SOC = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RivianBattery::transmit_can(unsigned long currentMillis) {
|
||||
// Send 500ms CAN Message, too fast and the pack can't change states (pre-charge is built in, seems we can't change state during pre-charge)
|
||||
// 100ms seems to draw too much current for a 5A supply during contactor pull in
|
||||
if (currentMillis - previousMillis10 >= (INTERVAL_200_MS)) {
|
||||
previousMillis10 = currentMillis;
|
||||
|
||||
//If we want to close contactors, and preconditions are met
|
||||
if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) {
|
||||
//Standby -> Ready Mode
|
||||
if (BMS_state == STANDBY || BMS_state == SLEEP) {
|
||||
RIVIAN_150.data.u8[0] = 0x03;
|
||||
RIVIAN_150.data.u8[2] = 0x01;
|
||||
RIVIAN_420.data.u8[0] = 0x02;
|
||||
RIVIAN_200.data.u8[0] = 0x08;
|
||||
|
||||
transmit_can_frame(&RIVIAN_150);
|
||||
transmit_can_frame(&RIVIAN_420);
|
||||
transmit_can_frame(&RIVIAN_41F);
|
||||
transmit_can_frame(&RIVIAN_207);
|
||||
transmit_can_frame(&RIVIAN_200);
|
||||
}
|
||||
//Ready mode -> Go Mode
|
||||
|
||||
if (BMS_state == READY) {
|
||||
RIVIAN_150.data.u8[0] = 0x3E;
|
||||
RIVIAN_150.data.u8[2] = 0x03;
|
||||
RIVIAN_420.data.u8[0] = 0x03;
|
||||
|
||||
transmit_can_frame(&RIVIAN_150);
|
||||
transmit_can_frame(&RIVIAN_420);
|
||||
}
|
||||
|
||||
} else { //If we want to open contactors, transition the other way
|
||||
//Go mode -> Ready Mode
|
||||
|
||||
if (BMS_state == GO) {
|
||||
RIVIAN_150.data.u8[0] = 0x03;
|
||||
RIVIAN_150.data.u8[2] = 0x01;
|
||||
|
||||
transmit_can_frame(&RIVIAN_150);
|
||||
}
|
||||
|
||||
if (BMS_state == READY) {
|
||||
RIVIAN_150.data.u8[0] = 0x03;
|
||||
RIVIAN_150.data.u8[2] = 0x01;
|
||||
RIVIAN_420.data.u8[0] = 0x01;
|
||||
RIVIAN_200.data.u8[0] = 0x10;
|
||||
|
||||
transmit_can_frame(&RIVIAN_245);
|
||||
transmit_can_frame(&RIVIAN_150);
|
||||
transmit_can_frame(&RIVIAN_420);
|
||||
transmit_can_frame(&RIVIAN_41F);
|
||||
transmit_can_frame(&RIVIAN_200);
|
||||
}
|
||||
}
|
||||
//disabled this because the battery didn't like it so fast (slowed to 100ms) and because the battery couldn't change states fast enough
|
||||
//as much as I don't like the "free-running" aspect of it, checking the BMS_state and acting on it should fix issues caused by that.
|
||||
//transmit_can_frame(&RIVIAN_150);
|
||||
//transmit_can_frame(&RIVIAN_420);
|
||||
//transmit_can_frame(&RIVIAN_41F);
|
||||
//transmit_can_frame(&RIVIAN_207);
|
||||
//transmit_can_frame(&RIVIAN_200);
|
||||
}
|
||||
}
|
||||
|
||||
void RivianBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.number_of_cells = 108;
|
||||
datalayer.battery.info.total_capacity_Wh = 135000;
|
||||
datalayer.battery.info.chemistry = NMC;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
60
Software/src/battery/RIVIAN-BATTERY.h
Normal file
60
Software/src/battery/RIVIAN-BATTERY.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef RIVIAN_BATTERY_H
|
||||
#define RIVIAN_BATTERY_H
|
||||
#include "CanBattery.h"
|
||||
|
||||
class RivianBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
static constexpr const char* Name = "Rivian R1T large 135kWh battery";
|
||||
|
||||
private:
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4480;
|
||||
static const int MIN_PACK_VOLTAGE_DV = 2920;
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 3300; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
uint8_t BMS_state = 0;
|
||||
uint16_t battery_voltage = 3700;
|
||||
uint16_t battery_SOC = 5000;
|
||||
int32_t battery_current = 32000;
|
||||
uint16_t kWh_available_total = 135;
|
||||
uint16_t kWh_available_max = 135;
|
||||
int16_t battery_min_temperature = 0;
|
||||
int16_t battery_max_temperature = 0;
|
||||
uint16_t battery_discharge_limit_amp = 0;
|
||||
uint16_t battery_charge_limit_amp = 0;
|
||||
static const uint8_t SLEEP = 0;
|
||||
static const uint8_t STANDBY = 1;
|
||||
static const uint8_t READY = 2;
|
||||
static const uint8_t GO = 3;
|
||||
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
|
||||
|
||||
CAN_frame RIVIAN_150 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x150,
|
||||
.data = {0x03, 0x00, 0x01, 0x00, 0x01, 0x00}};
|
||||
CAN_frame RIVIAN_420 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x420,
|
||||
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame RIVIAN_41F = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x41F, .data = {0x62, 0x10, 0x00}};
|
||||
CAN_frame RIVIAN_245 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x245,
|
||||
.data = {0x10, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame RIVIAN_200 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x200, .data = {0x08}};
|
||||
CAN_frame RIVIAN_207 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 1,
|
||||
.ID = 0x207,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}};
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,4 +1,5 @@
|
|||
#include "RJXZS-BMS.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
@ -32,20 +33,20 @@ void RjxzsBms::update_values() {
|
|||
datalayer.battery.status.current_dA = total_current;
|
||||
}
|
||||
|
||||
// Charge power is set in .h file
|
||||
// Charge power is manually set
|
||||
if (datalayer.battery.status.real_soc > 9900) {
|
||||
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W;
|
||||
} else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) {
|
||||
// When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
MAX_CHARGE_POWER_ALLOWED_W *
|
||||
datalayer.battery.status.override_charge_power_W *
|
||||
(1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
|
||||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W;
|
||||
datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W;
|
||||
}
|
||||
|
||||
// Discharge power is also set in .h file
|
||||
datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W;
|
||||
// Discharge power is manually set
|
||||
datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W;
|
||||
|
||||
uint16_t temperatures[] = {
|
||||
module_1_temperature, module_2_temperature, module_3_temperature, module_4_temperature,
|
||||
|
@ -72,14 +73,8 @@ void RjxzsBms::update_values() {
|
|||
|
||||
datalayer.battery.status.temperature_max_dC = max_temp;
|
||||
|
||||
// The cellvoltages[] array can contain 0s inside it
|
||||
populated_cellvoltages = 0;
|
||||
for (int i = 0; i < MAX_AMOUNT_CELLS; ++i) {
|
||||
if (cellvoltages[i] > 0) { // We have a measurement available
|
||||
datalayer.battery.status.cell_voltages_mV[populated_cellvoltages] = cellvoltages[i];
|
||||
populated_cellvoltages++;
|
||||
}
|
||||
}
|
||||
//Map all cell voltages to the global array
|
||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, MAX_AMOUNT_CELLS * sizeof(uint16_t));
|
||||
|
||||
datalayer.battery.info.number_of_cells = populated_cellvoltages; // 1-192S
|
||||
|
||||
|
@ -145,262 +140,18 @@ void RjxzsBms::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
charging_active = false;
|
||||
discharging_active = true;
|
||||
}
|
||||
} else if (mux == 0x07) { // Cellvoltages 1-3
|
||||
cellvoltages[0] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[1] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[2] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x08) { // Cellvoltages 4-6
|
||||
cellvoltages[3] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[4] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[5] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x09) { // Cellvoltages 7-9
|
||||
cellvoltages[6] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[7] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[8] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0A) { // Cellvoltages 10-12
|
||||
cellvoltages[9] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[10] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[11] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0B) { // Cellvoltages 13-15
|
||||
cellvoltages[12] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[13] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[14] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0C) { // Cellvoltages 16-18
|
||||
cellvoltages[15] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[16] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[17] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0D) { // Cellvoltages 19-21
|
||||
cellvoltages[18] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[19] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[20] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0E) { // Cellvoltages 22-24
|
||||
cellvoltages[21] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[22] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[23] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x0F) { // Cellvoltages 25-27
|
||||
cellvoltages[24] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[25] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[26] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x10) { // Cellvoltages 28-30
|
||||
cellvoltages[27] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[28] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[29] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x11) { // Cellvoltages 31-33
|
||||
cellvoltages[30] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[31] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[32] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x12) { // Cellvoltages 34-36
|
||||
cellvoltages[33] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[34] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[35] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x13) { // Cellvoltages 37-39
|
||||
cellvoltages[36] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[37] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[38] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x14) { // Cellvoltages 40-42
|
||||
cellvoltages[39] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[40] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[41] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x15) { // Cellvoltages 43-45
|
||||
cellvoltages[42] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[43] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[44] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x16) { // Cellvoltages 46-48
|
||||
cellvoltages[45] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[46] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[47] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x17) { // Cellvoltages 49-51
|
||||
cellvoltages[48] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[49] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[50] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x18) { // Cellvoltages 52-54
|
||||
cellvoltages[51] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[52] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[53] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x19) { // Cellvoltages 55-57
|
||||
cellvoltages[54] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[55] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[56] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1A) { // Cellvoltages 58-60
|
||||
cellvoltages[57] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[58] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[59] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1B) { // Cellvoltages 61-63
|
||||
cellvoltages[60] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[61] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[62] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1C) { // Cellvoltages 64-66
|
||||
cellvoltages[63] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[64] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[65] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1D) { // Cellvoltages 67-69
|
||||
cellvoltages[66] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[67] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[68] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1E) { // Cellvoltages 70-72
|
||||
cellvoltages[69] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[70] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[71] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x1F) { // Cellvoltages 73-75
|
||||
cellvoltages[72] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[73] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[74] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x20) { // Cellvoltages 76-78
|
||||
cellvoltages[75] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[76] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[77] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x21) { // Cellvoltages 79-81
|
||||
cellvoltages[78] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[79] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[80] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x22) { // Cellvoltages 82-84
|
||||
cellvoltages[81] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[82] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[83] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x23) { // Cellvoltages 85-87
|
||||
cellvoltages[84] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[85] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[86] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x24) { // Cellvoltages 88-90
|
||||
cellvoltages[87] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[88] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[89] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x25) { // Cellvoltages 91-93
|
||||
cellvoltages[90] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[91] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[92] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x26) { // Cellvoltages 94-96
|
||||
cellvoltages[93] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[94] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[95] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x27) { // Cellvoltages 97-99
|
||||
cellvoltages[96] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[97] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[98] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x28) { // Cellvoltages 100-102
|
||||
cellvoltages[99] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[100] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[101] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x29) { // Cellvoltages 103-105
|
||||
cellvoltages[102] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[103] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[104] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2A) { // Cellvoltages 106-108
|
||||
cellvoltages[105] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[106] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[107] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2B) { // Cellvoltages 109-111
|
||||
cellvoltages[108] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[109] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[110] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2C) { // Cellvoltages 112-114
|
||||
cellvoltages[111] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[112] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[113] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2D) { // Cellvoltages 115-117
|
||||
cellvoltages[114] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[115] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[116] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2E) { // Cellvoltages 118-120
|
||||
cellvoltages[117] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[118] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[119] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x2F) { // Cellvoltages 121-123
|
||||
cellvoltages[120] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[121] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[122] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x30) { // Cellvoltages 124-126
|
||||
cellvoltages[123] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[124] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[125] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x31) { // Cellvoltages 127-129
|
||||
cellvoltages[126] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[127] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[128] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x32) { // Cellvoltages 130-132
|
||||
cellvoltages[129] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[130] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[131] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x33) { // Cellvoltages 133-135
|
||||
cellvoltages[132] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[133] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[134] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x34) { // Cellvoltages 136-138
|
||||
cellvoltages[135] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[136] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[137] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x35) { // Cellvoltages 139-141
|
||||
cellvoltages[138] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[139] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[140] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x36) { // Cellvoltages 142-144
|
||||
cellvoltages[141] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[142] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[143] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x37) { // Cellvoltages 145-147
|
||||
cellvoltages[144] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[145] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[146] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x38) { // Cellvoltages 148-150
|
||||
cellvoltages[147] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[148] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[149] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x39) { // Cellvoltages 151-153
|
||||
cellvoltages[150] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[151] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[152] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3A) { // Cellvoltages 154-156
|
||||
cellvoltages[153] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[154] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[155] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3B) { // Cellvoltages 157-159
|
||||
cellvoltages[156] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[157] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[158] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3C) { // Cellvoltages 160-162
|
||||
cellvoltages[159] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[160] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[161] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3D) { // Cellvoltages 163-165
|
||||
cellvoltages[162] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[163] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[164] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3E) { // Cellvoltages 166-167
|
||||
cellvoltages[165] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[166] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[167] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x3F) { // Cellvoltages 169-171
|
||||
cellvoltages[168] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[169] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[170] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x40) { // Cellvoltages 172-174
|
||||
cellvoltages[171] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[172] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[173] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x41) { // Cellvoltages 175-177
|
||||
cellvoltages[174] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[175] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[176] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x42) { // Cellvoltages 178-180
|
||||
cellvoltages[177] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[178] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[179] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x43) { // Cellvoltages 181-183
|
||||
cellvoltages[180] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[181] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[182] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x44) { // Cellvoltages 184-186
|
||||
cellvoltages[183] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[184] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[185] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x45) { // Cellvoltages 187-189
|
||||
cellvoltages[186] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[187] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[188] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux == 0x46) { // Cellvoltages 190-192
|
||||
cellvoltages[189] = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2];
|
||||
cellvoltages[190] = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
cellvoltages[191] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
|
||||
} else if (mux >= 0x07 && mux <= 0x46) {
|
||||
// Cell voltages 1-192 (3 per message, 0x07=1-3, 0x08=4-6, ..., 0x46=190-192)
|
||||
int cell_index = (mux - 0x07) * 3;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (cell_index + i >= MAX_AMOUNT_CELLS) {
|
||||
break;
|
||||
}
|
||||
cellvoltages[cell_index + i] = (rx_frame.data.u8[1 + i * 2] << 8) | rx_frame.data.u8[2 + i * 2];
|
||||
}
|
||||
if (cell_index + 2 >= populated_cellvoltages) {
|
||||
populated_cellvoltages = cell_index + 2 + 1;
|
||||
}
|
||||
} else if (mux == 0x47) {
|
||||
temperature_below_zero_mod1_4 = rx_frame.data.u8[2];
|
||||
module_1_temperature = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||
|
@ -517,9 +268,9 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) {
|
|||
void RjxzsBms::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "../system_settings.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef RJXZS_BMS
|
||||
#define SELECTED_BATTERY_CLASS RjxzsBms
|
||||
#endif
|
||||
|
||||
class RjxzsBms : public CanBattery {
|
||||
public:
|
||||
RjxzsBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
|
||||
|
@ -19,14 +15,6 @@ class RjxzsBms : public CanBattery {
|
|||
static constexpr const char* Name = "RJXZS BMS, DIY battery";
|
||||
|
||||
private:
|
||||
/* Tweak these according to your battery build */
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000;
|
||||
static const int MAX_CHARGE_POWER_ALLOWED_W = 5000;
|
||||
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
|
||||
static const int RAMPDOWN_SOC =
|
||||
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||
|
@ -41,7 +29,7 @@ class RjxzsBms : public CanBattery {
|
|||
|
||||
uint8_t mux = 0;
|
||||
bool setup_completed = false;
|
||||
uint16_t total_voltage = 0;
|
||||
uint16_t total_voltage = 3700;
|
||||
int16_t total_current = 0;
|
||||
uint16_t total_power = 0;
|
||||
uint16_t battery_usage_capacity = 0;
|
||||
|
@ -83,9 +71,9 @@ class RjxzsBms : public CanBattery {
|
|||
uint16_t low_voltage_power_outage_delayed = 0;
|
||||
uint16_t num_of_triggering_protection_cells = 0;
|
||||
uint16_t balanced_reference_voltage = 0;
|
||||
uint16_t minimum_cell_voltage = 0;
|
||||
uint16_t maximum_cell_voltage = 0;
|
||||
uint16_t cellvoltages[MAX_AMOUNT_CELLS];
|
||||
uint16_t minimum_cell_voltage = 3300;
|
||||
uint16_t maximum_cell_voltage = 3300;
|
||||
uint16_t cellvoltages[MAX_AMOUNT_CELLS] = {0};
|
||||
uint8_t populated_cellvoltages = 0;
|
||||
uint16_t accumulated_total_capacity_high = 0;
|
||||
uint16_t accumulated_total_capacity_low = 0;
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef SAMSUNG_SDI_LV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS SamsungSdiLVBattery
|
||||
#endif
|
||||
|
||||
class SamsungSdiLVBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "SANTA-FE-PHEV-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef SANTA_FE_PHEV_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS SantaFePhevBattery
|
||||
#endif
|
||||
|
||||
class SantaFePhevBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "SIMPBMS-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
void SimpBmsBattery::update_values() {
|
||||
|
||||
datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00
|
||||
|
@ -39,9 +40,10 @@ void SimpBmsBattery::update_values() {
|
|||
}
|
||||
|
||||
void SimpBmsBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x355:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
SOC = (rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0];
|
||||
SOH = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2];
|
||||
|
||||
|
@ -93,10 +95,9 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) {
|
|||
void SimpBmsBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.number_of_cells = CELL_COUNT;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
|
||||
datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef SIMPBMS_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS SimpBmsBattery
|
||||
#endif
|
||||
|
||||
class SimpBmsBattery : public CanBattery {
|
||||
public:
|
||||
virtual void setup(void);
|
||||
|
@ -16,14 +12,6 @@ class SimpBmsBattery : public CanBattery {
|
|||
static constexpr const char* Name = "SIMPBMS battery";
|
||||
|
||||
private:
|
||||
/* DEFAULT VALUES BMS will send configured */
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
static const int MAX_CELL_DEVIATION_MV = 500;
|
||||
static const int CELL_COUNT = 96;
|
||||
|
||||
static const int SIMPBMS_MAX_CELLS = 128;
|
||||
|
||||
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "SONO-BATTERY.h"
|
||||
#include <cstring> //For unit test
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue