mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Merge branch 'main' into feature/bolt-ampera-battery
This commit is contained in:
commit
6535e96804
97 changed files with 7834 additions and 3412 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -23,6 +23,6 @@
|
|||
- Inverter communication protocol: ``
|
||||
- Hardware used for Battery-Emulator: `HW_LILYGO, HW_STARK, Custom`
|
||||
- CONTACTOR_CONTROL: `yes/no`
|
||||
- DUAL_CAN: `yes/no`
|
||||
- CAN_ADDON: `yes/no`
|
||||
- WEBSERVER: `yes/no`
|
||||
- MQTT: `yes/no`
|
||||
|
|
32
.github/workflows/compile-all-batteries.yml
vendored
32
.github/workflows/compile-all-batteries.yml
vendored
|
@ -9,12 +9,28 @@ on:
|
|||
- pull_request
|
||||
|
||||
# This is the list of jobs that will be run concurrently.
|
||||
# Since we use a build matrix, the actual number of jobs
|
||||
# started depends on how many configurations the matrix
|
||||
# will produce.
|
||||
jobs:
|
||||
# This is the name of the job
|
||||
# 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.
|
||||
build-batteries:
|
||||
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.
|
||||
|
@ -40,9 +56,10 @@ jobs:
|
|||
- CHADEMO_BATTERY
|
||||
- IMIEV_CZERO_ION_BATTERY
|
||||
- JAGUAR_IPACE_BATTERY
|
||||
- KIA_HYUNDAI_64_BATTERY
|
||||
- KIA_E_GMP_BATTERY
|
||||
- KIA_HYUNDAI_64_BATTERY
|
||||
- KIA_HYUNDAI_HYBRID_BATTERY
|
||||
- MEB_BATTERY
|
||||
- MG_5_BATTERY
|
||||
- NISSAN_LEAF_BATTERY
|
||||
- PYLON_BATTERY
|
||||
|
@ -54,6 +71,7 @@ jobs:
|
|||
- RENAULT_ZOE_GEN2_BATTERY
|
||||
- SANTA_FE_PHEV_BATTERY
|
||||
- TESLA_MODEL_3Y_BATTERY
|
||||
- TESLA_MODEL_SX_BATTERY
|
||||
- VOLVO_SPA_BATTERY
|
||||
- TEST_FAKE_BATTERY
|
||||
- SERIAL_LINK_RECEIVER
|
||||
|
@ -68,6 +86,10 @@ jobs:
|
|||
# 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.
|
||||
|
|
115
.github/workflows/compile-all-combinations-part1-batteries-A-to-M.yml
vendored
Normal file
115
.github/workflows/compile-all-combinations-part1-batteries-A-to-M.yml
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
# This is the name of the workflow, visible on GitHub UI.
|
||||
name: Compile All Combinations
|
||||
|
||||
# Here we tell GitHub when to run the workflow.
|
||||
on:
|
||||
# This allows you to run this workflow manually from the
|
||||
# GitHub Actions tab.
|
||||
workflow_dispatch:
|
||||
# The workflow is run upon creating, editing,
|
||||
# pre-releasing, releasing and publishing a release
|
||||
release:
|
||||
types: [created, edited, prereleased, released, published]
|
||||
|
||||
# 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-matrix:
|
||||
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:
|
||||
- BMW_I3_BATTERY
|
||||
- BMW_IX_BATTERY
|
||||
- BYD_ATTO_3_BATTERY
|
||||
- CELLPOWER_BMS
|
||||
- CHADEMO_BATTERY
|
||||
- IMIEV_CZERO_ION_BATTERY
|
||||
- JAGUAR_IPACE_BATTERY
|
||||
- KIA_E_GMP_BATTERY
|
||||
- KIA_HYUNDAI_64_BATTERY
|
||||
- KIA_HYUNDAI_HYBRID_BATTERY
|
||||
- MEB_BATTERY
|
||||
- MG_5_BATTERY
|
||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||
inverter:
|
||||
- AFORE_CAN
|
||||
- BYD_CAN
|
||||
- BYD_KOSTAL_RS485
|
||||
- BYD_MODBUS
|
||||
- BYD_SMA
|
||||
- FOXESS_CAN
|
||||
- PYLON_CAN
|
||||
- PYLON_LV_CAN
|
||||
- SCHNEIDER_CAN
|
||||
- SMA_CAN
|
||||
- SMA_LV_CAN
|
||||
- SMA_TRIPOWER_CAN
|
||||
- SOFAR_CAN
|
||||
- SOLAX_CAN
|
||||
- SERIAL_LINK_TRANSMITTER
|
||||
|
||||
# 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.extra_flags=-Wall -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}}" ./Software
|
|
@ -12,12 +12,30 @@ on:
|
|||
types: [created, edited, prereleased, released, published]
|
||||
|
||||
# This is the list of jobs that will be run concurrently.
|
||||
# Since we use a build matrix, the actual number of jobs
|
||||
# started depends on how many configurations the matrix
|
||||
# will produce.
|
||||
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-matrix:
|
||||
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.
|
||||
|
@ -36,30 +54,32 @@ jobs:
|
|||
#- esp32:esp32:esp32s3
|
||||
# These are the batteries for which the code will be compiled.
|
||||
battery:
|
||||
- BMW_I3_BATTERY
|
||||
- BYD_ATTO_3_BATTERY
|
||||
- CHADEMO_BATTERY
|
||||
- IMIEV_CZERO_ION_BATTERY
|
||||
- KIA_HYUNDAI_64_BATTERY
|
||||
- KIA_HYUNDAI_HYBRID_BATTERY
|
||||
- NISSAN_LEAF_BATTERY
|
||||
- PYLON_BATTERY
|
||||
- RJXZS_BMS
|
||||
- RANGE_ROVER_PHEV_BATTERY
|
||||
- RENAULT_KANGOO_BATTERY
|
||||
- RENAULT_TWIZY_BATTERY
|
||||
- RENAULT_ZOE_GEN1_BATTERY
|
||||
- RENAULT_ZOE_GEN2_BATTERY
|
||||
- SANTA_FE_PHEV_BATTERY
|
||||
- TESLA_MODEL_3Y_BATTERY
|
||||
- TESLA_MODEL_SX_BATTERY
|
||||
- VOLVO_SPA_BATTERY
|
||||
- TEST_FAKE_BATTERY
|
||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||
inverter:
|
||||
- AFORE_CAN
|
||||
- BYD_CAN
|
||||
- BYD_KOSTAL_RS485
|
||||
- BYD_SMA
|
||||
- BYD_MODBUS
|
||||
- BYD_SMA
|
||||
- FOXESS_CAN
|
||||
- PYLON_CAN
|
||||
- PYLON_LV_CAN
|
||||
- SCHNEIDER_CAN
|
||||
- SMA_CAN
|
||||
- SMA_LV_CAN
|
||||
- SMA_TRIPOWER_CAN
|
||||
- SOFAR_CAN
|
||||
- SOLAX_CAN
|
||||
|
@ -73,6 +93,10 @@ jobs:
|
|||
# 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.
|
41
.github/workflows/compile-all-inverters.yml
vendored
41
.github/workflows/compile-all-inverters.yml
vendored
|
@ -9,12 +9,30 @@ on:
|
|||
- pull_request
|
||||
|
||||
# This is the list of jobs that will be run concurrently.
|
||||
# Since we use a build matrix, the actual number of jobs
|
||||
# started depends on how many configurations the matrix
|
||||
# will produce.
|
||||
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-inverters:
|
||||
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.
|
||||
|
@ -33,30 +51,25 @@ jobs:
|
|||
#- esp32:esp32:esp32s3
|
||||
# These are the batteries for which the code will be compiled.
|
||||
battery:
|
||||
# - BMW_I3_BATTERY
|
||||
# - CHADEMO_BATTERY
|
||||
# - IMIEV_CZERO_ION_BATTERY
|
||||
# - KIA_HYUNDAI_64_BATTERY
|
||||
- NISSAN_LEAF_BATTERY
|
||||
# - RENAULT_ZOE_BATTERY
|
||||
# - TESLA_MODEL_3Y_BATTERY
|
||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||
inverter:
|
||||
- AFORE_CAN
|
||||
- BYD_CAN
|
||||
- BYD_KOSTAL_RS485
|
||||
- BYD_SMA
|
||||
- BYD_MODBUS
|
||||
- BYD_SMA
|
||||
- FOXESS_CAN
|
||||
- PYLON_LV_CAN
|
||||
- PYLON_CAN
|
||||
- PYLON_LV_CAN
|
||||
- SCHNEIDER_CAN
|
||||
- SMA_CAN
|
||||
- SMA_LV_CAN
|
||||
- SMA_TRIPOWER_CAN
|
||||
- SOFAR_CAN
|
||||
- SOLAX_CAN
|
||||
- SERIAL_LINK_TRANSMITTER
|
||||
- NISSANLEAF_CHARGER # Last element is not an inverter, but good to also test if charger compiles
|
||||
- NISSANLEAF_CHARGER # Last element is not an inverter, but good to also test if the charger compiles
|
||||
# This is the platform GitHub will use to run our workflow.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
@ -65,6 +78,10 @@ jobs:
|
|||
# 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.
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -22,3 +22,6 @@ compile.bat
|
|||
|
||||
# Ignore binary files
|
||||
*.bin
|
||||
|
||||
# Ignore secret file
|
||||
USER_SECRETS.h
|
|
@ -16,7 +16,7 @@ This software enables EV battery packs to be used for stationary storage. It ach
|
|||
## Hardware requirements 📜
|
||||
This code fits on the LilyGo ESP32 T-CAN485 devboard , see https://github.com/Xinyuan-LilyGO/T-CAN485
|
||||
|
||||
You will also need a complete EV battery. [See the battery compability list on which are supported.](https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki#supported-batteries-list)
|
||||
You will also need a complete EV battery. [See the battery compatibility list on which are supported.](https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki#supported-batteries-list)
|
||||
|
||||
Finally, you will need a [compatible hybrid solar inverter](https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki#supported-inverters-list), for example the "Fronius Gen24" or "GoodWe ET"
|
||||
|
||||
|
@ -36,7 +36,7 @@ Here's how to wire up the communication between the components.
|
|||
Here's how to connect the high voltage lines
|
||||

|
||||
|
||||
For more examples showing wiring, see each battery types own Wiki page. For instance the [Nissan LEAF page](https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki/Nissan-LEAF-battery#wiring-diagram)
|
||||
For more examples showing wiring, see each battery types 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 💻
|
||||
1. Download the Arduino IDE: https://www.arduino.cc/en/software
|
||||
|
@ -54,7 +54,8 @@ For more examples showing wiring, see each battery types own Wiki page. For inst
|
|||
5. The Arduino board should be set to `ESP32 Dev Module` (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. Press `Verify` and `Upload` to send the sketch to the board.
|
||||
7. Copy the `USER_SECRETS.TEMPLATE.h` file to `USER_SECRETS.h` and update relevant secrets.
|
||||
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
|
||||
|
@ -83,7 +84,7 @@ This code uses the following excellent libraries:
|
|||
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
||||
- [knolleary/pubsubclient](https://github.com/knolleary/pubsubclient) MIT-License
|
||||
- [mackelec/SerialDataLink](https://github.com/mackelec/SerialDataLink)
|
||||
- [me-no-dev/AsyncTCP](https://github.com/me-no-dev/AsyncTCP) LGPL-3.0 license
|
||||
- [mathieucarbou/AsyncTCP](https://github.com/mathieucarbou/AsyncTCP) LGPL-3.0 license
|
||||
- [me-no-dev/ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
|
||||
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
||||
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
||||
|
|
|
@ -11,9 +11,16 @@
|
|||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "src/charger/CHARGERS.h"
|
||||
#include "src/communication/can/comm_can.h"
|
||||
#include "src/communication/contactorcontrol/comm_contactorcontrol.h"
|
||||
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
|
||||
#include "src/communication/nvm/comm_nvm.h"
|
||||
#include "src/communication/rs485/comm_rs485.h"
|
||||
#include "src/communication/seriallink/comm_seriallink.h"
|
||||
#include "src/datalayer/datalayer.h"
|
||||
#include "src/devboard/utils/events.h"
|
||||
#include "src/devboard/utils/led_handler.h"
|
||||
#include "src/devboard/utils/logging.h"
|
||||
#include "src/devboard/utils/value_mapping.h"
|
||||
#include "src/lib/YiannisBourkelis-Uptime-Library/src/uptime.h"
|
||||
#include "src/lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h"
|
||||
|
@ -41,52 +48,14 @@
|
|||
#endif // MQTT
|
||||
#endif // WIFI
|
||||
|
||||
#ifndef CONTACTOR_CONTROL
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
#error CONTACTOR_CONTROL needs to be enabled for PWM_CONTACTOR_CONTROL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#include "src/devboard/utils/debounce_button.h"
|
||||
#endif
|
||||
|
||||
Preferences settings; // Store user settings
|
||||
// The current software version, shown on webserver
|
||||
const char* version_number = "7.9.dev";
|
||||
const char* version_number = "8.0.dev";
|
||||
|
||||
// Interval settings
|
||||
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
|
||||
unsigned long previousMillis10ms = 0;
|
||||
unsigned long previousMillisUpdateVal = 0;
|
||||
|
||||
// CAN parameters
|
||||
CAN_device_t CAN_cfg; // CAN Config
|
||||
const int rx_queue_size = 10; // Receive Queue size
|
||||
volatile bool send_ok = 0;
|
||||
|
||||
#ifdef DUAL_CAN
|
||||
#include "src/lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
|
||||
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
|
||||
static ACAN2515_Buffer16 gBuffer;
|
||||
#endif //DUAL_CAN
|
||||
#ifdef CAN_FD
|
||||
#include "src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
|
||||
ACAN2517FD canfd(MCP2517_CS, SPI, MCP2517_INT);
|
||||
#endif //CAN_FD
|
||||
|
||||
// ModbusRTU parameters
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
#define MB_RTU_NUM_VALUES 13100
|
||||
uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
||||
ModbusServerRTU MBserver(Serial2, 2000);
|
||||
#endif
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
#define SERIAL_LINK_BAUDRATE 112500
|
||||
#endif
|
||||
|
||||
// Common charger parameters
|
||||
volatile float charger_setpoint_HV_VDC = 0.0f;
|
||||
volatile float charger_setpoint_HV_IDC = 0.0f;
|
||||
|
@ -113,65 +82,11 @@ MyTimer loop_task_timer_10s(INTERVAL_10_S);
|
|||
|
||||
MyTimer check_pause_2s(INTERVAL_2_S);
|
||||
|
||||
// Contactor parameters
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||
State contactorStatus = DISCONNECTED;
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic
|
||||
#undef ON
|
||||
#define ON 0
|
||||
#undef OFF
|
||||
#define OFF 1
|
||||
#endif
|
||||
|
||||
#define MAX_ALLOWED_FAULT_TICKS 1000
|
||||
/* NOTE: modify the precharge time constant below to account for the resistance and capacitance of the target system.
|
||||
* t=3RC at minimum, t=5RC ideally
|
||||
*/
|
||||
#define PRECHARGE_TIME_MS 160
|
||||
#define NEGATIVE_CONTACTOR_TIME_MS 1000
|
||||
#define POSITIVE_CONTACTOR_TIME_MS 2000
|
||||
#define PWM_Freq 20000 // 20 kHz frequency, beyond audible range
|
||||
#define PWM_Res 10 // 10 Bit resolution 0 to 1023, maps 'nicely' to 0% 100%
|
||||
#define PWM_HOLD_DUTY 250
|
||||
#define PWM_OFF_DUTY 0
|
||||
#define PWM_ON_DUTY 1023
|
||||
#define POSITIVE_PWM_Ch 0
|
||||
#define NEGATIVE_PWM_Ch 1
|
||||
unsigned long prechargeStartTime = 0;
|
||||
unsigned long negativeStartTime = 0;
|
||||
unsigned long timeSpentInFaultedMode = 0;
|
||||
#endif
|
||||
|
||||
void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFFFFFFFF) {
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (pwm_freq != 0xFFFFFFFFFF) {
|
||||
ledcWrite(pin, pwm_freq);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (direction == 1) {
|
||||
digitalWrite(pin, HIGH);
|
||||
} else { // 0
|
||||
digitalWrite(pin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
const unsigned long equipment_button_long_press_duration =
|
||||
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
|
||||
const unsigned long equipment_button_debounce_duration = 200; // 250ms for debouncing the button
|
||||
unsigned long timeSincePress = 0; // Variable to store the time since the last press
|
||||
DebouncedButton equipment_stop_button; // Debounced button object
|
||||
#endif
|
||||
|
||||
TaskHandle_t main_loop_task;
|
||||
TaskHandle_t connectivity_loop_task;
|
||||
|
||||
Logging logging;
|
||||
|
||||
// Initialization
|
||||
void setup() {
|
||||
init_serial();
|
||||
|
@ -277,24 +192,24 @@ void core_loop(void* task_time_us) {
|
|||
|
||||
// Input, Runs as fast as possible
|
||||
receive_can_native(); // Receive CAN messages from native CAN port
|
||||
#ifdef CAN_FD
|
||||
receive_canfd(); // Receive CAN-FD messages.
|
||||
#endif
|
||||
#ifdef DUAL_CAN
|
||||
receive_can_addonMCP2515(); // Receive CAN messages on add-on MCP2515 chip
|
||||
#endif
|
||||
#ifdef CANFD_ADDON
|
||||
receive_canfd_addon(); // Receive CAN-FD messages.
|
||||
#endif // CANFD_ADDON
|
||||
#ifdef CAN_ADDON
|
||||
receive_can_addon(); // Receive CAN messages on add-on MCP2515 chip
|
||||
#endif // CAN_ADDON
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
receive_RS485(); // Process serial2 RS485 interface
|
||||
#endif
|
||||
#endif // RS485_INVERTER_SELECTED
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
runSerialDataLink();
|
||||
#endif
|
||||
run_serialDataLink();
|
||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
||||
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
||||
#ifdef WEBSERVER
|
||||
START_TIME_MEASUREMENT(ota);
|
||||
ElegantOTA.loop();
|
||||
END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us);
|
||||
#endif
|
||||
#endif // WEBSERVER
|
||||
|
||||
START_TIME_MEASUREMENT(time_10ms);
|
||||
// Process
|
||||
|
@ -312,12 +227,12 @@ void core_loop(void* task_time_us) {
|
|||
#ifdef DOUBLE_BATTERY
|
||||
update_values_battery2();
|
||||
check_interconnect_available();
|
||||
#endif
|
||||
#endif // DOUBLE_BATTERY
|
||||
update_calculated_values();
|
||||
#ifndef SERIAL_LINK_RECEIVER
|
||||
update_machineryprotection(); // Check safeties (Not on serial link reciever board)
|
||||
#endif
|
||||
update_values_inverter(); // Update values heading towards inverter
|
||||
#endif // SERIAL_LINK_RECEIVER
|
||||
update_values_inverter(); // Update values heading towards inverter
|
||||
if (DUMMY_EVENT_ENABLED) {
|
||||
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
|
||||
}
|
||||
|
@ -331,7 +246,6 @@ void core_loop(void* task_time_us) {
|
|||
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
||||
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
||||
#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;
|
||||
|
@ -353,8 +267,7 @@ void core_loop(void* task_time_us) {
|
|||
datalayer.system.status.time_cantx_us = 0;
|
||||
datalayer.system.status.core_task_10s_max_us = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // FUNCTION_TIME_MEASUREMENT
|
||||
if (check_pause_2s.elapsed()) {
|
||||
emulator_pause_state_send_CAN_battery();
|
||||
}
|
||||
|
@ -370,395 +283,9 @@ void init_serial() {
|
|||
while (!Serial) {}
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("__ OK __");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
|
||||
void init_stored_settings() {
|
||||
static uint32_t temp = 0;
|
||||
settings.begin("batterySettings", false);
|
||||
|
||||
// Always get the equipment stop status
|
||||
datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false);
|
||||
if (datalayer.system.settings.equipment_stop_active) {
|
||||
set_event(EVENT_EQUIPMENT_STOP, 1);
|
||||
}
|
||||
|
||||
#ifndef LOAD_SAVED_SETTINGS_ON_BOOT
|
||||
settings.clear(); // If this clear function is executed, no settings will be read from storage
|
||||
|
||||
//always save the equipment stop status
|
||||
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WIFI
|
||||
|
||||
char tempSSIDstring[63]; // Allocate buffer with sufficient size
|
||||
size_t lengthSSID = settings.getString("SSID", tempSSIDstring, sizeof(tempSSIDstring));
|
||||
if (lengthSSID > 0) { // Successfully read the string from memory. Set it to SSID!
|
||||
ssid = tempSSIDstring;
|
||||
} else { // Reading from settings failed. Do nothing with SSID. Raise event?
|
||||
}
|
||||
char tempPasswordString[63]; // Allocate buffer with sufficient size
|
||||
size_t lengthPassword = settings.getString("PASSWORD", tempPasswordString, sizeof(tempPasswordString));
|
||||
if (lengthPassword > 7) { // Successfully read the string from memory. Set it to password!
|
||||
password = tempPasswordString;
|
||||
} else { // Reading from settings failed. Do nothing with SSID. Raise event?
|
||||
}
|
||||
#endif
|
||||
|
||||
temp = settings.getUInt("BATTERY_WH_MAX", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.info.total_capacity_Wh = temp;
|
||||
}
|
||||
temp = settings.getUInt("MAXPERCENTAGE", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_percentage = temp * 10; // Multiply by 10 for backwards compatibility
|
||||
}
|
||||
temp = settings.getUInt("MINPERCENTAGE", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.min_percentage = temp * 10; // Multiply by 10 for backwards compatibility
|
||||
}
|
||||
temp = settings.getUInt("MAXCHARGEAMP", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_charge_dA = temp;
|
||||
}
|
||||
temp = settings.getUInt("MAXDISCHARGEAMP", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_discharge_dA = temp;
|
||||
temp = settings.getBool("USE_SCALED_SOC", false);
|
||||
datalayer.battery.settings.soc_scaling_active = temp; //This bool needs to be checked inside the temp!= block
|
||||
} // No way to know if it wasnt reset otherwise
|
||||
|
||||
settings.end();
|
||||
}
|
||||
|
||||
void init_CAN() {
|
||||
// CAN pins
|
||||
#ifdef CAN_SE_PIN
|
||||
pinMode(CAN_SE_PIN, OUTPUT);
|
||||
digitalWrite(CAN_SE_PIN, LOW);
|
||||
#endif
|
||||
CAN_cfg.speed = CAN_SPEED_500KBPS;
|
||||
#ifdef NATIVECAN_250KBPS // Some component is requesting lower CAN speed
|
||||
CAN_cfg.speed = CAN_SPEED_250KBPS;
|
||||
#endif
|
||||
CAN_cfg.tx_pin_id = CAN_TX_PIN;
|
||||
CAN_cfg.rx_pin_id = CAN_RX_PIN;
|
||||
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
|
||||
// Init CAN Module
|
||||
ESP32Can.CANInit();
|
||||
|
||||
#ifdef DUAL_CAN
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||
#endif
|
||||
gBuffer.initWithSize(25);
|
||||
SPI.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
|
||||
ACAN2515Settings settings(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
|
||||
settings.mRequestedMode = ACAN2515Settings::NormalMode;
|
||||
const uint16_t errorCodeMCP = can.begin(settings, [] { can.isr(); });
|
||||
if (errorCodeMCP == 0) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Can ok");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("Error Can: 0x");
|
||||
Serial.println(errorCodeMCP, HEX);
|
||||
#endif
|
||||
set_event(EVENT_CANMCP_INIT_FAILURE, (uint8_t)errorCodeMCP);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CAN_FD
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||
#endif
|
||||
SPI.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
|
||||
ACAN2517FDSettings settings(CAN_FD_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
|
||||
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
settings.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
|
||||
#else
|
||||
settings.mRequestedMode = ACAN2517FDSettings::NormalFD; // ListenOnly / Normal20B / NormalFD
|
||||
#endif
|
||||
const uint32_t errorCode = canfd.begin(settings, [] { canfd.isr(); });
|
||||
canfd.poll();
|
||||
if (errorCode == 0) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("Bit Rate prescaler: ");
|
||||
Serial.println(settings.mBitRatePrescaler);
|
||||
Serial.print("Arbitration Phase segment 1: ");
|
||||
Serial.println(settings.mArbitrationPhaseSegment1);
|
||||
Serial.print("Arbitration Phase segment 2: ");
|
||||
Serial.println(settings.mArbitrationPhaseSegment2);
|
||||
Serial.print("Arbitration SJW:");
|
||||
Serial.println(settings.mArbitrationSJW);
|
||||
Serial.print("Actual Arbitration Bit Rate: ");
|
||||
Serial.print(settings.actualArbitrationBitRate());
|
||||
Serial.println(" bit/s");
|
||||
Serial.print("Exact Arbitration Bit Rate ? ");
|
||||
Serial.println(settings.exactArbitrationBitRate() ? "yes" : "no");
|
||||
Serial.print("Arbitration Sample point: ");
|
||||
Serial.print(settings.arbitrationSamplePointFromBitStart());
|
||||
Serial.println("%");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("CAN-FD Configuration error 0x");
|
||||
Serial.println(errorCode, HEX);
|
||||
#endif
|
||||
set_event(EVENT_CANFD_INIT_FAILURE, (uint8_t)errorCode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_contactors() {
|
||||
// Init contactor pins
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
||||
POSITIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
||||
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
||||
NEGATIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Positive PWM to 0%
|
||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Negative PWM to 0%
|
||||
#else //Normal CONTACTOR_CONTROL
|
||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
#endif
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
#endif //CONTACTOR_CONTROL
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
|
||||
pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
// Init BMS contactor
|
||||
#ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor
|
||||
pinMode(BMS_POWER, OUTPUT);
|
||||
digitalWrite(BMS_POWER, HIGH);
|
||||
#endif //HW_STARK
|
||||
}
|
||||
|
||||
void init_rs485() {
|
||||
// Set up Modbus RTU Server
|
||||
#ifdef RS485_EN_PIN
|
||||
pinMode(RS485_EN_PIN, OUTPUT);
|
||||
digitalWrite(RS485_EN_PIN, HIGH);
|
||||
#endif
|
||||
#ifdef RS485_SE_PIN
|
||||
pinMode(RS485_SE_PIN, OUTPUT);
|
||||
digitalWrite(RS485_SE_PIN, HIGH);
|
||||
#endif
|
||||
#ifdef PIN_5V_EN
|
||||
pinMode(PIN_5V_EN, OUTPUT);
|
||||
digitalWrite(PIN_5V_EN, HIGH);
|
||||
#endif
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
Serial2.begin(57600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
#ifdef BYD_MODBUS
|
||||
// Init Static data to the RTU Modbus
|
||||
handle_static_data_modbus_byd();
|
||||
#endif
|
||||
|
||||
// Init Serial2 connected to the RTU Modbus
|
||||
RTUutils::prepareHardwareSerial(Serial2);
|
||||
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
// Register served function code worker for server
|
||||
MBserver.registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
|
||||
MBserver.registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
|
||||
MBserver.registerWorker(MBTCP_ID, WRITE_MULT_REGISTERS, &FC16);
|
||||
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
||||
// Start ModbusRTU background task
|
||||
MBserver.begin(Serial2, MODBUS_CORE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
|
||||
void monitor_equipment_stop_button() {
|
||||
|
||||
ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress);
|
||||
|
||||
if (equipment_stop_behavior == LATCHING_SWITCH) {
|
||||
if (changed_state == PRESSED) {
|
||||
// Changed to ON – initiating equipment stop.
|
||||
setBatteryPause(true, false, true);
|
||||
} else if (changed_state == RELEASED) {
|
||||
// Changed to OFF – ending equipment stop.
|
||||
setBatteryPause(false, false, false);
|
||||
}
|
||||
} else if (equipment_stop_behavior == MOMENTARY_SWITCH) {
|
||||
if (changed_state == RELEASED) { // button is released
|
||||
|
||||
if (timeSincePress < equipment_button_long_press_duration) {
|
||||
// Short press detected, trigger equipment stop
|
||||
setBatteryPause(true, false, true);
|
||||
} else {
|
||||
// Long press detected, reset equipment stop state
|
||||
setBatteryPause(false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_equipment_stop_button() {
|
||||
//using external pullup resistors NC
|
||||
pinMode(EQUIPMENT_STOP_PIN, INPUT);
|
||||
// Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time
|
||||
initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
enum frameDirection { MSG_RX, MSG_TX }; //RX = 0, TX = 1
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir);
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir) {
|
||||
#ifdef DEBUG_CAN_DATA // If enabled in user settings, print out the CAN messages via USB
|
||||
uint8_t i = 0;
|
||||
Serial.print(millis());
|
||||
Serial.print(" ");
|
||||
(msgDir == 0) ? Serial.print("RX ") : Serial.print("TX ");
|
||||
Serial.print(frame.ID, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(frame.DLC);
|
||||
Serial.print(" ");
|
||||
for (i = 0; i < frame.DLC; i++) {
|
||||
Serial.print(frame.data.u8[i] < 16 ? "0" : "");
|
||||
Serial.print(frame.data.u8[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println(" ");
|
||||
#endif //#DEBUG_CAN_DATA
|
||||
|
||||
if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording
|
||||
|
||||
char message_string[128]; // Buffer to hold the message string
|
||||
int offset = 0; // Keeps track of the current position in the buffer
|
||||
|
||||
// Add timestamp
|
||||
offset += snprintf(message_string + offset, sizeof(message_string) - offset, "%lu ", millis());
|
||||
|
||||
// Add direction
|
||||
offset +=
|
||||
snprintf(message_string + offset, sizeof(message_string) - offset, "%s ", (msgDir == MSG_RX) ? "RX" : "TX");
|
||||
|
||||
// Add ID and DLC
|
||||
offset += snprintf(message_string + offset, sizeof(message_string) - offset, "%X %u ", frame.ID, frame.DLC);
|
||||
|
||||
// Add data bytes
|
||||
for (uint8_t i = 0; i < frame.DLC; i++) {
|
||||
offset += snprintf(message_string + offset, sizeof(message_string) - offset, "%s%X ",
|
||||
frame.data.u8[i] < 16 ? "0" : "", frame.data.u8[i]);
|
||||
}
|
||||
// Add linebreak
|
||||
offset += snprintf(message_string + offset, sizeof(message_string) - offset, "\n");
|
||||
|
||||
// Ensure the string is null-terminated
|
||||
message_string[sizeof(message_string) - 1] = '\0';
|
||||
|
||||
// Append the message string to the system info structure
|
||||
size_t current_len =
|
||||
strnlen(datalayer.system.info.logged_can_messages, sizeof(datalayer.system.info.logged_can_messages));
|
||||
size_t available_space =
|
||||
sizeof(datalayer.system.info.logged_can_messages) - current_len - 1; // Space left for new data
|
||||
|
||||
if (available_space < strlen(message_string) + 1) {
|
||||
// Not enough space, reset and start from the beginning
|
||||
current_len = 0;
|
||||
datalayer.system.info.logged_can_messages[0] = '\0';
|
||||
}
|
||||
|
||||
strncat(datalayer.system.info.logged_can_messages, message_string, available_space);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CAN_FD
|
||||
// Functions
|
||||
void receive_canfd() { // This section checks if we have a complete CAN-FD message incoming
|
||||
CANFDMessage frame;
|
||||
if (canfd.available()) {
|
||||
canfd.receive(frame);
|
||||
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = frame.id;
|
||||
rx_frame.ext_ID = frame.ext;
|
||||
rx_frame.DLC = frame.len;
|
||||
for (uint8_t i = 0; i < rx_frame.DLC && i < 64; i++) {
|
||||
rx_frame.data.u8[i] = frame.data[i];
|
||||
}
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CAN_ADDON_FD_MCP2518);
|
||||
receive_can(&rx_frame, CANFD_NATIVE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void receive_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port
|
||||
CAN_frame_t rx_frame_native;
|
||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame_native, 0) == pdTRUE) {
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = rx_frame_native.MsgID;
|
||||
if (rx_frame_native.FIR.B.FF == CAN_frame_std) {
|
||||
rx_frame.ext_ID = false;
|
||||
} else { //CAN_frame_ext == 1
|
||||
rx_frame.ext_ID = true;
|
||||
}
|
||||
rx_frame.DLC = rx_frame_native.FIR.B.DLC;
|
||||
for (uint8_t i = 0; i < rx_frame.DLC && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = rx_frame_native.data.u8[i];
|
||||
}
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CAN_NATIVE);
|
||||
}
|
||||
}
|
||||
|
||||
void send_can() {
|
||||
if (!allowed_to_send_CAN) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_can_battery();
|
||||
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
send_can_inverter();
|
||||
#endif // CAN_INVERTER_SELECTED
|
||||
|
||||
#ifdef CHARGER_SELECTED
|
||||
send_can_charger();
|
||||
#endif // CHARGER_SELECTED
|
||||
}
|
||||
|
||||
#ifdef DUAL_CAN
|
||||
void receive_can_addonMCP2515() { // This section checks if we have a complete CAN message incoming on add-on CAN port
|
||||
CAN_frame rx_frame; // Struct with our CAN format
|
||||
CANMessage MCP2515Frame; // Struct with ACAN2515 library format, needed to use the MCP2515 library
|
||||
|
||||
if (can.available()) {
|
||||
can.receive(MCP2515Frame);
|
||||
|
||||
rx_frame.ID = MCP2515Frame.id;
|
||||
rx_frame.ext_ID = MCP2515Frame.ext ? CAN_frame_ext : CAN_frame_std;
|
||||
rx_frame.DLC = MCP2515Frame.len;
|
||||
for (uint8_t i = 0; i < MCP2515Frame.len && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = MCP2515Frame.data[i];
|
||||
}
|
||||
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CAN_ADDON_MCP2515);
|
||||
}
|
||||
}
|
||||
#endif // DUAL_CAN
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
void check_interconnect_available() {
|
||||
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
|
||||
|
@ -779,113 +306,7 @@ void check_interconnect_available() {
|
|||
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));
|
||||
}
|
||||
}
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
||||
void handle_contactors() {
|
||||
#ifdef BYD_SMA
|
||||
datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
|
||||
#endif
|
||||
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
handle_contactors_battery2();
|
||||
#endif
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
// First check if we have any active errors, incase we do, turn off the battery
|
||||
if (datalayer.battery.status.bms_status == FAULT) {
|
||||
timeSpentInFaultedMode++;
|
||||
} else {
|
||||
timeSpentInFaultedMode = 0;
|
||||
}
|
||||
|
||||
//handle contactor control SHUTDOWN_REQUESTED
|
||||
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
|
||||
contactorStatus = SHUTDOWN_REQUESTED;
|
||||
}
|
||||
|
||||
if (contactorStatus == SHUTDOWN_REQUESTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
|
||||
datalayer.system.status.contactors_engaged = false;
|
||||
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
|
||||
}
|
||||
|
||||
// After that, check if we are OK to start turning on the battery
|
||||
if (contactorStatus == DISCONNECTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
|
||||
if (datalayer.system.status.battery_allows_contactor_closing &&
|
||||
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) {
|
||||
contactorStatus = PRECHARGE;
|
||||
}
|
||||
}
|
||||
|
||||
// In case the inverter requests contactors to open, set the state accordingly
|
||||
if (contactorStatus == COMPLETED) {
|
||||
//Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable)
|
||||
if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.settings.equipment_stop_active) {
|
||||
contactorStatus = DISCONNECTED;
|
||||
}
|
||||
// Skip running the state machine below if it has already completed
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long currentTime = millis();
|
||||
// Handle actual state machine. This first turns on Precharge, then Negative, then Positive, and finally turns OFF precharge
|
||||
switch (contactorStatus) {
|
||||
case PRECHARGE:
|
||||
set(PRECHARGE_PIN, ON);
|
||||
prechargeStartTime = currentTime;
|
||||
contactorStatus = NEGATIVE;
|
||||
break;
|
||||
|
||||
case NEGATIVE:
|
||||
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
negativeStartTime = currentTime;
|
||||
contactorStatus = POSITIVE;
|
||||
}
|
||||
break;
|
||||
|
||||
case POSITIVE:
|
||||
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
contactorStatus = PRECHARGE_OFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRECHARGE_OFF:
|
||||
if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
contactorStatus = COMPLETED;
|
||||
datalayer.system.status.contactors_engaged = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // CONTACTOR_CONTROL
|
||||
}
|
||||
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
void handle_contactors_battery2() {
|
||||
if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing) {
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, ON);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, ON);
|
||||
datalayer.system.status.contactors_battery2_engaged = true;
|
||||
} else { // Closing contactors on secondary battery not allowed
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, OFF);
|
||||
datalayer.system.status.contactors_battery2_engaged = false;
|
||||
}
|
||||
}
|
||||
#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
void update_calculated_values() {
|
||||
/* Calculate allowed charge/discharge currents*/
|
||||
|
@ -975,7 +396,7 @@ void update_calculated_values() {
|
|||
} else {
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
#endif
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
} else { // soc_scaling_active == false. No SOC window wanted. Set scaled to same as real.
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
|
@ -1004,65 +425,19 @@ void update_calculated_values() {
|
|||
datalayer.battery.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
#endif //DOUBLE_BATTERY
|
||||
#endif // DOUBLE_BATTERY
|
||||
}
|
||||
|
||||
void update_values_inverter() {
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
update_values_can_inverter();
|
||||
#endif
|
||||
#endif // CAN_INVERTER_SELECTED
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
update_modbus_registers_inverter();
|
||||
#endif
|
||||
#endif // CAN_INVERTER_SELECTED
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
update_RS485_registers_inverter();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
void runSerialDataLink() {
|
||||
static unsigned long updateTime = 0;
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
||||
updateTime = currentMillis;
|
||||
#ifdef SERIAL_LINK_RECEIVER
|
||||
manageSerialLinkReceiver();
|
||||
#endif
|
||||
#ifdef SERIAL_LINK_TRANSMITTER
|
||||
manageSerialLinkTransmitter();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_serialDataLink() {
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
Serial2.begin(SERIAL_LINK_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void store_settings_equipment_stop() {
|
||||
settings.begin("batterySettings", false);
|
||||
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
|
||||
settings.end();
|
||||
}
|
||||
|
||||
void storeSettings() {
|
||||
settings.begin("batterySettings", false);
|
||||
#ifdef WIFI
|
||||
settings.putString("SSID", String(ssid.c_str()));
|
||||
settings.putString("PASSWORD", String(password.c_str()));
|
||||
#endif
|
||||
settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh);
|
||||
settings.putUInt("MAXPERCENTAGE",
|
||||
datalayer.battery.settings.max_percentage / 10); // Divide by 10 for backwards compatibility
|
||||
settings.putUInt("MINPERCENTAGE",
|
||||
datalayer.battery.settings.min_percentage / 10); // Divide by 10 for backwards compatibility
|
||||
settings.putUInt("MAXCHARGEAMP", datalayer.battery.settings.max_user_set_charge_dA);
|
||||
settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.settings.max_user_set_discharge_dA);
|
||||
settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active);
|
||||
settings.end();
|
||||
#endif // CAN_INVERTER_SELECTED
|
||||
}
|
||||
|
||||
/** Reset reason numbering and description
|
||||
|
@ -1141,87 +516,3 @@ void check_reset_reason() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void transmit_can(CAN_frame* tx_frame, int interface) {
|
||||
if (!allowed_to_send_CAN) {
|
||||
return;
|
||||
}
|
||||
print_can_frame(*tx_frame, frameDirection(MSG_TX));
|
||||
|
||||
switch (interface) {
|
||||
case CAN_NATIVE:
|
||||
CAN_frame_t frame;
|
||||
frame.MsgID = tx_frame->ID;
|
||||
frame.FIR.B.FF = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
frame.FIR.B.DLC = tx_frame->DLC;
|
||||
frame.FIR.B.RTR = CAN_no_RTR;
|
||||
for (uint8_t i = 0; i < tx_frame->DLC; i++) {
|
||||
frame.data.u8[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
ESP32Can.CANWriteFrame(&frame);
|
||||
break;
|
||||
case CAN_ADDON_MCP2515: {
|
||||
#ifdef DUAL_CAN
|
||||
//Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2
|
||||
CANMessage MCP2515Frame;
|
||||
MCP2515Frame.id = tx_frame->ID;
|
||||
MCP2515Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2515Frame.len = tx_frame->DLC;
|
||||
MCP2515Frame.rtr = false;
|
||||
for (uint8_t i = 0; i < MCP2515Frame.len; i++) {
|
||||
MCP2515Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
can.tryToSend(MCP2515Frame);
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //DUAL_CAN
|
||||
} break;
|
||||
case CANFD_NATIVE:
|
||||
case CAN_ADDON_FD_MCP2518: {
|
||||
#ifdef CAN_FD
|
||||
CANFDMessage MCP2518Frame;
|
||||
MCP2518Frame.id = tx_frame->ID;
|
||||
MCP2518Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2518Frame.len = tx_frame->DLC;
|
||||
for (uint8_t i = 0; i < MCP2518Frame.len; i++) {
|
||||
MCP2518Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
send_ok = canfd.tryToSend(MCP2518Frame);
|
||||
if (!send_ok) {
|
||||
set_event(EVENT_CANFD_BUFFER_FULL, interface);
|
||||
}
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //CAN_FD
|
||||
} break;
|
||||
default:
|
||||
// Invalid interface sent with function call. TODO: Raise event that coders messed up
|
||||
break;
|
||||
}
|
||||
}
|
||||
void receive_can(CAN_frame* rx_frame, int interface) {
|
||||
|
||||
print_can_frame(*rx_frame, frameDirection(MSG_RX));
|
||||
|
||||
if (interface == can_config.battery) {
|
||||
receive_can_battery(*rx_frame);
|
||||
#ifdef CHADEMO_BATTERY
|
||||
ISA_handleFrame(rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.inverter) {
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
receive_can_inverter(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.battery_double) {
|
||||
#ifdef DOUBLE_BATTERY
|
||||
receive_can_battery2(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.charger) {
|
||||
#ifdef CHARGER_SELECTED
|
||||
receive_can_charger(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
13
Software/USER_SECRETS.TEMPLATE.h
Normal file
13
Software/USER_SECRETS.TEMPLATE.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID" // Maximum of 63 characters
|
||||
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD" // Minimum of 8 characters
|
||||
#define AP_PASSWORD "123456789" // Minimum of 8 characters; set to blank if you want the access point to be open
|
||||
|
||||
#define WEBSERVER_AUTH_REQUIRED \
|
||||
false //Set this line to true to activate webserver authentication (this line must not be commented).
|
||||
#define HTTP_USERNAME "admin" // username to webserver authentication;
|
||||
#define HTTP_PASSWORD "admin" // password to webserver authentication;
|
||||
|
||||
#define MQTT_SERVER "192.168.xxx.yyy" // mqtt server address
|
||||
#define MQTT_PORT 1883 // mqtt server port
|
||||
#define MQTT_USER NULL // mqtt username, leave blank for no authentication
|
||||
#define MQTT_PASSWORD NULL // mqtt password, leave blank for no authentication
|
|
@ -1,5 +1,6 @@
|
|||
#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 */
|
||||
|
@ -9,7 +10,7 @@
|
|||
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
|
||||
CAN_ADDON_FD_MCP2518 = Add-on CAN-FD MCP2518 connected to GPIO pins
|
||||
CANFD_ADDON_MCP2518 = Add-on CAN-FD MCP2518 connected to GPIO pins
|
||||
*/
|
||||
|
||||
volatile CAN_Configuration can_config = {
|
||||
|
@ -21,12 +22,12 @@ volatile CAN_Configuration can_config = {
|
|||
|
||||
#ifdef WIFI
|
||||
|
||||
volatile uint8_t AccessPointEnabled = true; //Set to either true/false to enable direct wifi access point
|
||||
std::string ssid = "REPLACE_WITH_YOUR_SSID"; // Maximum of 63 characters
|
||||
std::string password = "REPLACE_WITH_YOUR_PASSWORD"; // Minimum of 8 characters
|
||||
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters, also used for device name on web interface
|
||||
const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open
|
||||
const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection
|
||||
volatile uint8_t AccessPointEnabled = true; //Set to either true/false to enable direct wifi access point
|
||||
std::string ssid = WIFI_SSID; // Set in USER_SECRETS.h
|
||||
std::string password = WIFI_PASSWORD; // Set in USER_SECRETS.h
|
||||
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters, also used for device name on web interface
|
||||
const char* passwordAP = AP_PASSWORD; // Set in USER_SECRETS.h
|
||||
const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection
|
||||
|
||||
#ifdef WIFICONFIG
|
||||
// Set your Static IP address
|
||||
|
@ -37,14 +38,14 @@ IPAddress gateway(192, 168, 10, 1);
|
|||
IPAddress subnet(255, 255, 255, 0);
|
||||
#endif
|
||||
#ifdef WEBSERVER
|
||||
const char* http_username = "admin"; // username to webserver authentication;
|
||||
const char* http_password = "admin"; // password to webserver authentication;
|
||||
const char* http_username = HTTP_USERNAME; // Set in USER_SECRETS.h
|
||||
const char* http_password = HTTP_PASSWORD; // Set in USER_SECRETS.h
|
||||
|
||||
#endif // WEBSERVER
|
||||
// MQTT
|
||||
#ifdef MQTT
|
||||
const char* mqtt_user = "REDACTED"; // Set NULL for no username
|
||||
const char* mqtt_password = "REDACTED"; // Set NULL for no password
|
||||
const char* mqtt_user = MQTT_USER; // Set in USER_SECRETS.h
|
||||
const char* mqtt_password = MQTT_PASSWORD; // Set in USER_SECRETS.h
|
||||
#ifdef MQTT_MANUAL_TOPIC_OBJECT_NAME
|
||||
const char* mqtt_topic_name =
|
||||
"BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX"
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
|
||||
//#define IMIEV_CZERO_ION_BATTERY
|
||||
//#define JAGUAR_IPACE_BATTERY
|
||||
//#define KIA_HYUNDAI_64_BATTERY
|
||||
//#define KIA_E_GMP_BATTERY
|
||||
//#define KIA_HYUNDAI_64_BATTERY
|
||||
//#define KIA_HYUNDAI_HYBRID_BATTERY
|
||||
//#define MEB_BATTERY
|
||||
//#define MG_5_BATTERY
|
||||
//#define NISSAN_LEAF_BATTERY
|
||||
//#define PYLON_BATTERY
|
||||
|
@ -30,18 +31,18 @@
|
|||
//#define RENAULT_ZOE_GEN1_BATTERY
|
||||
//#define RENAULT_ZOE_GEN2_BATTERY
|
||||
//#define SANTA_FE_PHEV_BATTERY
|
||||
//#define TESLA_MODEL_SX_BATTERY
|
||||
//#define TESLA_MODEL_3Y_BATTERY
|
||||
//#define TESLA_MODEL_SX_BATTERY
|
||||
//#define VOLVO_SPA_BATTERY
|
||||
//#define TEST_FAKE_BATTERY
|
||||
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires DUAL_CAN setup)
|
||||
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires CAN_ADDON setup)
|
||||
|
||||
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/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_SMA //Enable this line to emulate a SMA compatible "BYD Battery-Box HVS 10.2KW battery" over CAN bus
|
||||
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
|
||||
//#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485
|
||||
//#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_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 BYD_SMA //Enable this line to emulate a SMA compatible "BYD Battery-Box HVS 10.2KW battery" over CAN bus
|
||||
//#define FOXESS_CAN //Enable this line to emulate a "HV2600/ECS4100 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
|
||||
|
@ -58,31 +59,35 @@
|
|||
//#define HW_3LB
|
||||
|
||||
/* 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!
|
||||
|
||||
/* Other options */
|
||||
//#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_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)
|
||||
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||
//#define DUAL_CAN //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 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency?
|
||||
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
|
||||
#ifdef CAN_FD // CAN_FD additional options if enabled
|
||||
#define CAN_FD_CRYSTAL_FREQUENCY_MHZ \
|
||||
ACAN2517FDSettings:: \
|
||||
OSC_40MHz //CAN_FD option, what is your MCP2518 add-on boards crystal frequency? (Default OSC_40MHz)
|
||||
//#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)
|
||||
#if defined(DEBUG_VIA_USB) || defined(DEBUG_VIA_WEB)
|
||||
#define DEBUG_LOG
|
||||
#endif
|
||||
|
||||
//#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)
|
||||
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||
//#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
|
||||
#ifdef CANFD_ADDON // CANFD_ADDON additional options if enabled
|
||||
#define CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ \
|
||||
ACAN2517FDSettings:: \
|
||||
OSC_40MHz //CANFD_ADDON option, what is your MCP2518 add-on boards crystal frequency? (Default OSC_40MHz)
|
||||
#endif // CANFD_ADDON
|
||||
//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN
|
||||
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
|
||||
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
|
||||
#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 WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
||||
#define WEBSERVER_AUTH_REQUIRED \
|
||||
false //Set this line to true to activate webserver authentication (this line must not be commented). Refer to USER_SETTINGS.cpp for setting the credentials.
|
||||
#define WIFIAP //Disable this line to permanently disable WIFI AP mode (make sure to hardcode ssid and password of you home wifi network). When enabled WIFI AP can still be disabled by a setting in the future.
|
||||
#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/battery settings set below)
|
||||
|
@ -91,8 +96,6 @@
|
|||
|
||||
/* MQTT options */
|
||||
// #define MQTT // Enable this line to enable MQTT
|
||||
#define MQTT_SERVER "192.168.xxx.yyy"
|
||||
#define MQTT_PORT 1883
|
||||
#define MQTT_MANUAL_TOPIC_OBJECT_NAME // Enable this 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. \
|
||||
|
@ -124,14 +127,20 @@
|
|||
#define BATTERY_MAXTEMPERATURE 500
|
||||
// -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below)
|
||||
#define BATTERY_MINTEMPERATURE -250
|
||||
// 300 = 30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
|
||||
// 300 = 30.0A , Max charge in Amp (Some inverters needs to be limited)
|
||||
#define BATTERY_MAX_CHARGE_AMP 300
|
||||
// 300 = 30.0A , BYD CAN specific setting, Max discharge in Amp (Some inverters needs to be limited)
|
||||
// 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
|
||||
|
||||
/* 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" */
|
||||
typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CAN_ADDON_FD_MCP2518 = 3 } CAN_Interface;
|
||||
typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CANFD_ADDON_MCP2518 = 3 } CAN_Interface;
|
||||
typedef struct {
|
||||
CAN_Interface battery;
|
||||
CAN_Interface inverter;
|
||||
|
|
|
@ -47,6 +47,10 @@
|
|||
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
|
||||
#endif
|
||||
|
||||
#ifdef MEB_BATTERY
|
||||
#include "MEB-BATTERY.h"
|
||||
#endif
|
||||
|
||||
#ifdef MG_5_BATTERY
|
||||
#include "MG-5-BATTERY.h"
|
||||
#endif
|
||||
|
|
|
@ -785,7 +785,7 @@ void receive_can_battery2(CAN_frame rx_frame) {
|
|||
datalayer.battery2.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
|
||||
datalayer.battery2.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
|
||||
datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
|
||||
datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
|
||||
datalayer.battery2.status.cell_voltages_mV[6] = ((rx_frame.data.u8[7] * 10) + 1800);
|
||||
}
|
||||
break;
|
||||
case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
|
||||
|
|
|
@ -670,8 +670,8 @@ void receive_can_battery(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_VIA_USB
|
||||
Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
|
||||
#endif
|
||||
//set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type
|
||||
transmit_can(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
|
||||
|
|
|
@ -118,7 +118,7 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.real_soc = estimateSOC(datalayer.battery.status.voltage_dV);
|
||||
SOC_method = ESTIMATED;
|
||||
#else // Pack is not crashed, we can use periodically transmitted SOC
|
||||
datalayer.battery.status.real_soc = battery_highprecision_SOC * 100;
|
||||
datalayer.battery.status.real_soc = battery_highprecision_SOC * 10;
|
||||
SOC_method = MEASURED;
|
||||
#endif
|
||||
|
||||
|
@ -419,6 +419,9 @@ void setup_battery(void) { // Performs one time setup at startup
|
|||
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;
|
||||
//Due to the Datalayer having 370.0V as startup value, which is 10V lower than the Atto 3 min voltage 380.0V
|
||||
//We now init the value to 380.1V to avoid false positive events.
|
||||
datalayer.battery.status.voltage_dV = MIN_PACK_VOLTAGE_DV + 1;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.info.number_of_cells = 126;
|
||||
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
|
||||
|
|
|
@ -197,13 +197,13 @@ inline void process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
x102_chg_session.ChargingCurrentRequest = newChargingCurrentRequest;
|
||||
x102_chg_session.TargetBatteryVoltage = newTargetBatteryVoltage;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#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;
|
||||
Serial.print("Charge Rate (kW): ");
|
||||
Serial.println(chargingrate);
|
||||
logging.print("Charge Rate (kW): ");
|
||||
logging.println(chargingrate);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -217,40 +217,40 @@ inline void 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_VIA_USB
|
||||
Serial.println("Inconsistent charge/discharge state.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inconsistent charge/discharge state.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryOverVoltage) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Vehicle indicates fault, battery over voltage.");
|
||||
#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_VIA_USB
|
||||
Serial.println("Vehicle indicates fault, battery under voltage.");
|
||||
#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_VIA_USB
|
||||
Serial.println("Vehicle indicates fault, battery current deviation. Possible EVSE issue?");
|
||||
#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_VIA_USB
|
||||
Serial.println("Vehicle indicates fault, battery voltage deviation. Possible EVSE issue?");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery voltage deviation. Possible EVSE issue?");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
|
@ -264,8 +264,8 @@ inline void 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_VIA_USB
|
||||
Serial.println("State of charge ceiling reached or charging interrupted, stop charging");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("State of charge ceiling reached or charging interrupted, stop charging");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
|
@ -273,8 +273,8 @@ inline void process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
|
||||
if (vehicle_permission && CHADEMO_Status == CHADEMO_NEGOTIATE) {
|
||||
CHADEMO_Status = CHADEMO_EV_ALLOWED;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("STATE shift to CHADEMO_EV_ALLOWED in process_vehicle_charging_session()");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("STATE shift to CHADEMO_EV_ALLOWED in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -284,22 +284,22 @@ inline void 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_VIA_USB
|
||||
Serial.println("STATE SHIFT to EVSE_START reached in process_vehicle_charging_session()");
|
||||
#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_VIA_USB
|
||||
Serial.println("updating vehicle request in process_vehicle_charging_session()");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("updating vehicle request in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("UNHANDLED STATE IN process_vehicle_charging_session()");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED STATE IN process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -312,20 +312,20 @@ inline void 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_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
/* unsigned long currentMillis = millis();
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
Serial.println("x200 Max remaining capacity for charging/discharging:");
|
||||
logging.println("x200 Max remaining capacity for charging/discharging:");
|
||||
// initially this is set to 0, which is represented as 0xFF
|
||||
Serial.println(0xFF - x200_discharge_limits.MaxRemainingCapacityForCharging);
|
||||
logging.println(0xFF - x200_discharge_limits.MaxRemainingCapacityForCharging);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
@ -341,13 +341,13 @@ inline void 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_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
Serial.print("x201 availabile vehicle energy, completion time: ");
|
||||
Serial.println(x201_discharge_estimate.AvailableVehicleEnergy);
|
||||
Serial.print("x201 approx vehicle completion time: ");
|
||||
Serial.println(x201_discharge_estimate.ApproxDischargeCompletionTime);
|
||||
logging.print("x201 availabile vehicle energy, completion time: ");
|
||||
logging.println(x201_discharge_estimate.AvailableVehicleEnergy);
|
||||
logging.print("x201 approx vehicle completion time: ");
|
||||
logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -369,17 +369,17 @@ inline void process_vehicle_vendor_ID(CAN_frame rx_frame) {
|
|||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
#ifdef CH_CAN_DEBUG
|
||||
Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.ID, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.DLC);
|
||||
Serial.print(" ");
|
||||
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.ID, HEX);
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.DLC);
|
||||
logging.print(" ");
|
||||
for (int i = 0; i < rx_frame.DLC; ++i) {
|
||||
Serial.print(rx_frame.data.u8[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(rx_frame.data.u8[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
#endif
|
||||
|
||||
// CHADEMO coexists with a CAN-based shunt. Only process CHADEMO-specific IDs
|
||||
|
@ -713,9 +713,9 @@ void send_can_battery() {
|
|||
// 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_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
//FIXME REMOVE
|
||||
Serial.println("REMOVE: proto 2.0");
|
||||
logging.println("REMOVE: proto 2.0");
|
||||
#endif
|
||||
transmit_can(&CHADEMO_118, can_config.battery);
|
||||
}
|
||||
|
@ -753,15 +753,15 @@ void handle_chademo_sequence() {
|
|||
/* ------------------- State override conditions checks ------------------- */
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && x102_chg_session.s.status.StatusVehicleShifterPosition) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Vehicle is not parked, abort.");
|
||||
#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_VIA_USB
|
||||
Serial.println("Vehicle charge/discharge permission ended, stop.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle charge/discharge permission ended, stop.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
@ -775,24 +775,24 @@ void handle_chademo_sequence() {
|
|||
plug_inserted = digitalRead(CHADEMO_PIN_7);
|
||||
|
||||
if (!plug_inserted) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// Serial.println("CHADEMO plug is not inserted.");
|
||||
// logging.println("CHADEMO plug is not inserted.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
CHADEMO_Status = CHADEMO_CONNECTED;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CHADEMO plug is inserted. Provide EVSE power to vehicle to trigger initialization.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO plug is inserted. Provide EVSE power to vehicle to trigger initialization.");
|
||||
#endif
|
||||
|
||||
break;
|
||||
case CHADEMO_CONNECTED:
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
//Serial.println("CHADEMO_CONNECTED State");
|
||||
//logging.println("CHADEMO_CONNECTED State");
|
||||
#endif
|
||||
/* plug_inserted is .. essentially a volatile of sorts, so verify */
|
||||
if (plug_inserted) {
|
||||
|
@ -810,8 +810,8 @@ void handle_chademo_sequence() {
|
|||
* with timers to have higher confidence of certain conditions hitting
|
||||
* a steady state
|
||||
*/
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CHADEMO plug is not inserted, cannot connect d2 relay to begin initialization.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO plug is not inserted, cannot connect d2 relay to begin initialization.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_IDLE;
|
||||
}
|
||||
|
@ -821,8 +821,8 @@ void handle_chademo_sequence() {
|
|||
* Used for triggers/error handling elsewhere;
|
||||
* State change to CHADEMO_NEGOTIATE occurs in receive_can_battery(..)
|
||||
*/
|
||||
#ifdef DEBUG_VIA_USB
|
||||
// Serial.println("Awaiting initial vehicle CAN to trigger negotiation");
|
||||
#ifdef DEBUG_LOG
|
||||
// logging.println("Awaiting initial vehicle CAN to trigger negotiation");
|
||||
#endif
|
||||
evse_init();
|
||||
break;
|
||||
|
@ -830,16 +830,16 @@ void handle_chademo_sequence() {
|
|||
/* Vehicle and EVSE dance */
|
||||
//TODO if pin 4 / j goes high,
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// Serial.println("CHADEMO_NEGOTIATE State");
|
||||
// logging.println("CHADEMO_NEGOTIATE State");
|
||||
#endif
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
break;
|
||||
case CHADEMO_EV_ALLOWED:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_EV_ALLOWED State");
|
||||
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
|
||||
|
@ -855,9 +855,9 @@ void handle_chademo_sequence() {
|
|||
}
|
||||
break;
|
||||
case CHADEMO_EVSE_PREPARE:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_EVSE_PREPARE State");
|
||||
logging.println("CHADEMO_EVSE_PREPARE State");
|
||||
#endif
|
||||
/* TODO voltage check of output < 20v
|
||||
* insulation test hypothetically happens here before triggering PIN 10 high
|
||||
|
@ -878,7 +878,7 @@ void handle_chademo_sequence() {
|
|||
digitalWrite(CHADEMO_PIN_10, HIGH);
|
||||
evse_permission = true;
|
||||
} else {
|
||||
Serial.println("Insulation check measures > 20v ");
|
||||
logging.println("Insulation check measures > 20v ");
|
||||
}
|
||||
|
||||
// likely unnecessary but just to be sure. consider removal
|
||||
|
@ -891,9 +891,9 @@ void handle_chademo_sequence() {
|
|||
//state changes to CHADEMO_EVSE_START only upon receipt of charging session request
|
||||
break;
|
||||
case CHADEMO_EVSE_START:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_EVSE_START State");
|
||||
logging.println("CHADEMO_EVSE_START State");
|
||||
#endif
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
|
@ -901,8 +901,8 @@ void handle_chademo_sequence() {
|
|||
|
||||
CHADEMO_Status = CHADEMO_EVSE_CONTACTORS_ENABLED;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Initiating contactors");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Initiating contactors");
|
||||
#endif
|
||||
|
||||
/* break rather than fall through because contactors are not instantaneous;
|
||||
|
@ -911,17 +911,17 @@ void handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_EVSE_CONTACTORS_ENABLED:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_EVSE_CONTACTORS State");
|
||||
logging.println("CHADEMO_EVSE_CONTACTORS State");
|
||||
#endif
|
||||
|
||||
/* check whether contactors ready, because externally dependent upon inverter allow during discharge */
|
||||
if (contactors_ready) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Contactors ready");
|
||||
Serial.print("Voltage: ");
|
||||
Serial.println(get_measured_voltage());
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Contactors ready");
|
||||
logging.print("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 &&
|
||||
|
@ -941,9 +941,9 @@ void handle_chademo_sequence() {
|
|||
/* break or fall through ? TODO */
|
||||
break;
|
||||
case CHADEMO_POWERFLOW:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_POWERFLOW State");
|
||||
logging.println("CHADEMO_POWERFLOW State");
|
||||
#endif
|
||||
/* POWERFLOW for charging, discharging, and bidirectional */
|
||||
/* Interpretation */
|
||||
|
@ -961,8 +961,8 @@ void handle_chademo_sequence() {
|
|||
}
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
@ -972,9 +972,9 @@ void handle_chademo_sequence() {
|
|||
x109_evse_state.s.status.EVSE_status = 1;
|
||||
break;
|
||||
case CHADEMO_STOP:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_STOP State");
|
||||
logging.println("CHADEMO_STOP State");
|
||||
#endif
|
||||
/* back to CHADEMO_IDLE after teardown */
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
|
@ -1000,16 +1000,16 @@ void handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_FAULT:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
Serial.println("CHADEMO_FAULT State");
|
||||
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_VIA_USB
|
||||
Serial.println("CHADEMO fault encountered, tearing down to make safe");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO fault encountered, tearing down to make safe");
|
||||
#endif
|
||||
digitalWrite(CHADEMO_PIN_10, LOW);
|
||||
digitalWrite(CHADEMO_PIN_2, LOW);
|
||||
|
@ -1020,8 +1020,8 @@ void handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("UNHANDLED CHADEMO_STATE, setting FAULT");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED CHADEMO_STATE, setting FAULT");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
break;
|
||||
|
|
|
@ -91,17 +91,17 @@ void ISA_handleFrame(CAN_frame* frame) {
|
|||
|
||||
case 0x510:
|
||||
case 0x511:
|
||||
Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
Serial.print(" ");
|
||||
Serial.print(frame->ID, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(frame->DLC);
|
||||
Serial.print(" ");
|
||||
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) {
|
||||
Serial.print(frame->data.u8[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(frame->data.u8[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
break;
|
||||
|
||||
case 0x521:
|
||||
|
@ -245,8 +245,8 @@ void ISA_initialize() {
|
|||
ISA_STOP();
|
||||
delay(500);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Serial.print("ISA Initialization ");
|
||||
Serial.println(i);
|
||||
logging.print("ISA Initialization ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x20 + i);
|
||||
outframe.data.u8[1] = 0x02;
|
||||
|
@ -271,7 +271,7 @@ void ISA_initialize() {
|
|||
}
|
||||
|
||||
void ISA_STOP() {
|
||||
Serial.println("ISA STOP");
|
||||
logging.println("ISA STOP");
|
||||
|
||||
outframe.data.u8[0] = 0x34;
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
@ -286,7 +286,7 @@ void ISA_STOP() {
|
|||
}
|
||||
|
||||
void ISA_sendSTORE() {
|
||||
Serial.println("ISA send STORE");
|
||||
logging.println("ISA send STORE");
|
||||
|
||||
outframe.data.u8[0] = 0x32;
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
@ -301,7 +301,7 @@ void ISA_sendSTORE() {
|
|||
}
|
||||
|
||||
void ISA_START() {
|
||||
Serial.println("ISA START");
|
||||
logging.println("ISA START");
|
||||
|
||||
outframe.data.u8[0] = 0x34;
|
||||
outframe.data.u8[1] = 0x01;
|
||||
|
@ -317,7 +317,7 @@ void ISA_START() {
|
|||
|
||||
void ISA_RESTART() {
|
||||
//Has the effect of zeroing AH and KWH
|
||||
Serial.println("ISA RESTART");
|
||||
logging.println("ISA RESTART");
|
||||
|
||||
outframe.data.u8[0] = 0x3F;
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
@ -336,7 +336,7 @@ void ISA_deFAULT() {
|
|||
ISA_STOP();
|
||||
delay(500);
|
||||
|
||||
Serial.println("ISA RESTART to default");
|
||||
logging.println("ISA RESTART to default");
|
||||
|
||||
outframe.data.u8[0] = 0x3D;
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
@ -358,7 +358,7 @@ void ISA_initCurrent() {
|
|||
ISA_STOP();
|
||||
delay(500);
|
||||
|
||||
Serial.println("ISA Initialization Current");
|
||||
logging.println("ISA Initialization Current");
|
||||
|
||||
outframe.data.u8[0] = 0x21;
|
||||
outframe.data.u8[1] = 0x02;
|
||||
|
@ -382,8 +382,8 @@ void ISA_initCurrent() {
|
|||
}
|
||||
|
||||
void ISA_getCONFIG(uint8_t i) {
|
||||
Serial.print("ISA Get Config ");
|
||||
Serial.println(i);
|
||||
logging.print("ISA Get Config ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x60 + i);
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
@ -398,8 +398,8 @@ void ISA_getCONFIG(uint8_t i) {
|
|||
}
|
||||
|
||||
void ISA_getCAN_ID(uint8_t i) {
|
||||
Serial.print("ISA Get CAN ID ");
|
||||
Serial.println(i);
|
||||
logging.print("ISA Get CAN ID ");
|
||||
logging.println(i);
|
||||
|
||||
outframe.data.u8[0] = (0x50 + i);
|
||||
if (i == 8)
|
||||
|
@ -418,8 +418,8 @@ void ISA_getCAN_ID(uint8_t i) {
|
|||
}
|
||||
|
||||
void ISA_getINFO(uint8_t i) {
|
||||
Serial.print("ISA Get INFO ");
|
||||
Serial.println(i, HEX);
|
||||
logging.print("ISA Get INFO ");
|
||||
logging.println(i, HEX);
|
||||
|
||||
outframe.data.u8[0] = (0x70 + i);
|
||||
outframe.data.u8[1] = 0x00;
|
||||
|
|
|
@ -103,29 +103,29 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
}
|
||||
|
||||
if (!BMU_Detected) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BMU not detected, check wiring!");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BMU not detected, check wiring!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Battery Values");
|
||||
Serial.print("BMU SOC: ");
|
||||
Serial.print(BMU_SOC);
|
||||
Serial.print(" BMU Current: ");
|
||||
Serial.print(BMU_Current);
|
||||
Serial.print(" BMU Battery Voltage: ");
|
||||
Serial.print(BMU_PackVoltage);
|
||||
Serial.print(" BMU_Power: ");
|
||||
Serial.print(BMU_Power);
|
||||
Serial.print(" Cell max voltage: ");
|
||||
Serial.print(max_volt_cel);
|
||||
Serial.print(" Cell min voltage: ");
|
||||
Serial.print(min_volt_cel);
|
||||
Serial.print(" Cell max temp: ");
|
||||
Serial.print(max_temp_cel);
|
||||
Serial.print(" Cell min temp: ");
|
||||
Serial.println(min_temp_cel);
|
||||
#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
|
||||
}
|
||||
|
||||
|
|
|
@ -57,9 +57,9 @@ CAN_frame ipace_keep_alive = {.FD = false,
|
|||
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/
|
||||
|
||||
void print_units(char* header, int value, char* units) {
|
||||
Serial.print(header);
|
||||
Serial.print(value);
|
||||
Serial.print(units);
|
||||
logging.print(header);
|
||||
logging.print(value);
|
||||
logging.print(units);
|
||||
}
|
||||
|
||||
void update_values_battery() {
|
||||
|
@ -104,8 +104,8 @@ void update_values_battery() {
|
|||
}
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Values going to inverter");
|
||||
#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 ");
|
||||
|
@ -115,7 +115,7 @@ void update_values_battery() {
|
|||
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 ");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -229,17 +229,17 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
}
|
||||
|
||||
// All CAN messages recieved will be logged via serial
|
||||
Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.ID, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.DLC);
|
||||
Serial.print(" ");
|
||||
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.ID, HEX);
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.DLC);
|
||||
logging.print(" ");
|
||||
for (int i = 0; i < rx_frame.DLC; ++i) {
|
||||
Serial.print(rx_frame.data.u8[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(rx_frame.data.u8[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
}
|
||||
|
||||
void send_can_battery() {
|
||||
|
|
|
@ -690,63 +690,63 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
|
||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println(); //sepatator
|
||||
Serial.println("Values from battery: ");
|
||||
Serial.print("SOC BMS: ");
|
||||
Serial.print((uint16_t)SOC_BMS / 10.0, 1);
|
||||
Serial.print("% | SOC Display: ");
|
||||
Serial.print((uint16_t)SOC_Display / 10.0, 1);
|
||||
Serial.print("% | SOH ");
|
||||
Serial.print((uint16_t)batterySOH / 10.0, 1);
|
||||
Serial.println("%");
|
||||
Serial.print((int16_t)batteryAmps / 10.0, 1);
|
||||
Serial.print(" Amps | ");
|
||||
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
|
||||
Serial.print(" Volts | ");
|
||||
Serial.print((int16_t)datalayer.battery.status.active_power_W);
|
||||
Serial.println(" Watts");
|
||||
Serial.print("Allowed Charge ");
|
||||
Serial.print((uint16_t)allowedChargePower * 10);
|
||||
Serial.print(" W | Allowed Discharge ");
|
||||
Serial.print((uint16_t)allowedDischargePower * 10);
|
||||
Serial.println(" W");
|
||||
Serial.print("MaxCellVolt ");
|
||||
Serial.print(CellVoltMax_mV);
|
||||
Serial.print(" mV No ");
|
||||
Serial.print(CellVmaxNo);
|
||||
Serial.print(" | MinCellVolt ");
|
||||
Serial.print(CellVoltMin_mV);
|
||||
Serial.print(" mV No ");
|
||||
Serial.println(CellVminNo);
|
||||
Serial.print("TempHi ");
|
||||
Serial.print((int16_t)temperatureMax);
|
||||
Serial.print("°C TempLo ");
|
||||
Serial.print((int16_t)temperatureMin);
|
||||
Serial.print("°C WaterInlet ");
|
||||
Serial.print((int8_t)temperature_water_inlet);
|
||||
Serial.print("°C PowerRelay ");
|
||||
Serial.print((int8_t)powerRelayTemperature * 2);
|
||||
Serial.println("°C");
|
||||
Serial.print("Aux12volt: ");
|
||||
Serial.print((int16_t)leadAcidBatteryVoltage / 10.0, 1);
|
||||
Serial.println("V | ");
|
||||
Serial.print("BmsManagementMode ");
|
||||
Serial.print((uint8_t)batteryManagementMode, BIN);
|
||||
#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) {
|
||||
Serial.print(" | BmsIgnition ON");
|
||||
logging.print(" | BmsIgnition ON");
|
||||
} else {
|
||||
Serial.print(" | BmsIgnition OFF");
|
||||
logging.print(" | BmsIgnition OFF");
|
||||
}
|
||||
|
||||
if (bitRead((uint8_t)batteryRelay, 0) == 1) {
|
||||
Serial.print(" | PowerRelay ON");
|
||||
logging.print(" | PowerRelay ON");
|
||||
} else {
|
||||
Serial.print(" | PowerRelay OFF");
|
||||
logging.print(" | PowerRelay OFF");
|
||||
}
|
||||
Serial.print(" | Inverter ");
|
||||
Serial.print(inverterVoltage);
|
||||
Serial.println(" Volts");
|
||||
logging.print(" | Inverter ");
|
||||
logging.print(inverterVoltage);
|
||||
logging.println(" Volts");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -808,7 +808,7 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
// print_canfd_frame(frame);
|
||||
switch (rx_frame.data.u8[0]) {
|
||||
case 0x10: //"PID Header"
|
||||
// Serial.println ("Send ack");
|
||||
// logging.println ("Send ack");
|
||||
poll_data_pid = rx_frame.data.u8[4];
|
||||
// if (rx_frame.data.u8[4] == poll_data_pid) {
|
||||
transmit_can(&EGMP_7E4_ack, can_config.battery); //Send ack to BMS if the same frame is sent as polled
|
||||
|
|
|
@ -142,63 +142,63 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
|
||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println(); //sepatator
|
||||
Serial.println("Values from battery: ");
|
||||
Serial.print("SOC BMS: ");
|
||||
Serial.print((uint16_t)SOC_BMS / 10.0, 1);
|
||||
Serial.print("% | SOC Display: ");
|
||||
Serial.print((uint16_t)SOC_Display / 10.0, 1);
|
||||
Serial.print("% | SOH ");
|
||||
Serial.print((uint16_t)batterySOH / 10.0, 1);
|
||||
Serial.println("%");
|
||||
Serial.print((int16_t)batteryAmps / 10.0, 1);
|
||||
Serial.print(" Amps | ");
|
||||
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
|
||||
Serial.print(" Volts | ");
|
||||
Serial.print((int16_t)datalayer.battery.status.active_power_W);
|
||||
Serial.println(" Watts");
|
||||
Serial.print("Allowed Charge ");
|
||||
Serial.print((uint16_t)allowedChargePower * 10);
|
||||
Serial.print(" W | Allowed Discharge ");
|
||||
Serial.print((uint16_t)allowedDischargePower * 10);
|
||||
Serial.println(" W");
|
||||
Serial.print("MaxCellVolt ");
|
||||
Serial.print(CellVoltMax_mV);
|
||||
Serial.print(" mV No ");
|
||||
Serial.print(CellVmaxNo);
|
||||
Serial.print(" | MinCellVolt ");
|
||||
Serial.print(CellVoltMin_mV);
|
||||
Serial.print(" mV No ");
|
||||
Serial.println(CellVminNo);
|
||||
Serial.print("TempHi ");
|
||||
Serial.print((int16_t)temperatureMax);
|
||||
Serial.print("°C TempLo ");
|
||||
Serial.print((int16_t)temperatureMin);
|
||||
Serial.print("°C WaterInlet ");
|
||||
Serial.print((int8_t)temperature_water_inlet);
|
||||
Serial.print("°C PowerRelay ");
|
||||
Serial.print((int8_t)powerRelayTemperature * 2);
|
||||
Serial.println("°C");
|
||||
Serial.print("Aux12volt: ");
|
||||
Serial.print((int16_t)leadAcidBatteryVoltage / 10.0, 1);
|
||||
Serial.println("V | ");
|
||||
Serial.print("BmsManagementMode ");
|
||||
Serial.print((uint8_t)batteryManagementMode, BIN);
|
||||
#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) {
|
||||
Serial.print(" | BmsIgnition ON");
|
||||
logging.print(" | BmsIgnition ON");
|
||||
} else {
|
||||
Serial.print(" | BmsIgnition OFF");
|
||||
logging.print(" | BmsIgnition OFF");
|
||||
}
|
||||
|
||||
if (bitRead((uint8_t)batteryRelay, 0) == 1) {
|
||||
Serial.print(" | PowerRelay ON");
|
||||
logging.print(" | PowerRelay ON");
|
||||
} else {
|
||||
Serial.print(" | PowerRelay OFF");
|
||||
logging.print(" | PowerRelay OFF");
|
||||
}
|
||||
Serial.print(" | Inverter ");
|
||||
Serial.print(inverterVoltage);
|
||||
Serial.println(" Volts");
|
||||
logging.print(" | Inverter ");
|
||||
logging.print(inverterVoltage);
|
||||
logging.println(" Volts");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
2132
Software/src/battery/MEB-BATTERY.cpp
Normal file
2132
Software/src/battery/MEB-BATTERY.cpp
Normal file
File diff suppressed because it is too large
Load diff
138
Software/src/battery/MEB-BATTERY.h
Normal file
138
Software/src/battery/MEB-BATTERY.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef MEB_BATTERY_H
|
||||
#define MEB_BATTERY_H
|
||||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define MAX_PACK_VOLTAGE_84S_DV 3528 //5000 = 500.0V
|
||||
#define MIN_PACK_VOLTAGE_84S_DV 2520
|
||||
#define MAX_PACK_VOLTAGE_96S_DV 4032
|
||||
#define MIN_PACK_VOLTAGE_96S_DV 2880
|
||||
#define MAX_PACK_VOLTAGE_108S_DV 4536
|
||||
#define MIN_PACK_VOLTAGE_108S_DV 3240
|
||||
#define MAX_CELL_DEVIATION_MV 150
|
||||
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
|
||||
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
#define PID_SOC 0x028C
|
||||
#define PID_VOLTAGE 0x1E3B
|
||||
#define PID_CURRENT 0x1E3D
|
||||
#define PID_MAX_TEMP 0x1E0E
|
||||
#define PID_MIN_TEMP 0x1E0F
|
||||
#define PID_MAX_CHARGE_VOLTAGE 0x5171
|
||||
#define PID_MIN_DISCHARGE_VOLTAGE 0x5170
|
||||
#define PID_ALLOWED_CHARGE_POWER 0x1E1B
|
||||
#define PID_ALLOWED_DISCHARGE_POWER 0x1E1C
|
||||
#define PID_CELLVOLTAGE_CELL_1 0x1E40
|
||||
#define PID_CELLVOLTAGE_CELL_2 0x1E41
|
||||
#define PID_CELLVOLTAGE_CELL_3 0x1E42
|
||||
#define PID_CELLVOLTAGE_CELL_4 0x1E43
|
||||
#define PID_CELLVOLTAGE_CELL_5 0x1E44
|
||||
#define PID_CELLVOLTAGE_CELL_6 0x1E45
|
||||
#define PID_CELLVOLTAGE_CELL_7 0x1E46
|
||||
#define PID_CELLVOLTAGE_CELL_8 0x1E47
|
||||
#define PID_CELLVOLTAGE_CELL_9 0x1E48
|
||||
#define PID_CELLVOLTAGE_CELL_10 0x1E49
|
||||
#define PID_CELLVOLTAGE_CELL_11 0x1E4A
|
||||
#define PID_CELLVOLTAGE_CELL_12 0x1E4B
|
||||
#define PID_CELLVOLTAGE_CELL_13 0x1E4C
|
||||
#define PID_CELLVOLTAGE_CELL_14 0x1E4D
|
||||
#define PID_CELLVOLTAGE_CELL_15 0x1E4E
|
||||
#define PID_CELLVOLTAGE_CELL_16 0x1E4F
|
||||
#define PID_CELLVOLTAGE_CELL_17 0x1E50
|
||||
#define PID_CELLVOLTAGE_CELL_18 0x1E51
|
||||
#define PID_CELLVOLTAGE_CELL_19 0x1E52
|
||||
#define PID_CELLVOLTAGE_CELL_20 0x1E53
|
||||
#define PID_CELLVOLTAGE_CELL_21 0x1E54
|
||||
#define PID_CELLVOLTAGE_CELL_22 0x1E55
|
||||
#define PID_CELLVOLTAGE_CELL_23 0x1E56
|
||||
#define PID_CELLVOLTAGE_CELL_24 0x1E57
|
||||
#define PID_CELLVOLTAGE_CELL_25 0x1E58
|
||||
#define PID_CELLVOLTAGE_CELL_26 0x1E59
|
||||
#define PID_CELLVOLTAGE_CELL_27 0x1E5A
|
||||
#define PID_CELLVOLTAGE_CELL_28 0x1E5B
|
||||
#define PID_CELLVOLTAGE_CELL_29 0x1E5C
|
||||
#define PID_CELLVOLTAGE_CELL_30 0x1E5D
|
||||
#define PID_CELLVOLTAGE_CELL_31 0x1E5E
|
||||
#define PID_CELLVOLTAGE_CELL_32 0x1E5F
|
||||
#define PID_CELLVOLTAGE_CELL_33 0x1E60
|
||||
#define PID_CELLVOLTAGE_CELL_34 0x1E61
|
||||
#define PID_CELLVOLTAGE_CELL_35 0x1E62
|
||||
#define PID_CELLVOLTAGE_CELL_36 0x1E63
|
||||
#define PID_CELLVOLTAGE_CELL_37 0x1E64
|
||||
#define PID_CELLVOLTAGE_CELL_38 0x1E65
|
||||
#define PID_CELLVOLTAGE_CELL_39 0x1E66
|
||||
#define PID_CELLVOLTAGE_CELL_40 0x1E67
|
||||
#define PID_CELLVOLTAGE_CELL_41 0x1E68
|
||||
#define PID_CELLVOLTAGE_CELL_42 0x1E69
|
||||
#define PID_CELLVOLTAGE_CELL_43 0x1E6A
|
||||
#define PID_CELLVOLTAGE_CELL_44 0x1E6B
|
||||
#define PID_CELLVOLTAGE_CELL_45 0x1E6C
|
||||
#define PID_CELLVOLTAGE_CELL_46 0x1E6D
|
||||
#define PID_CELLVOLTAGE_CELL_47 0x1E6E
|
||||
#define PID_CELLVOLTAGE_CELL_48 0x1E6F
|
||||
#define PID_CELLVOLTAGE_CELL_49 0x1E70
|
||||
#define PID_CELLVOLTAGE_CELL_50 0x1E71
|
||||
#define PID_CELLVOLTAGE_CELL_51 0x1E72
|
||||
#define PID_CELLVOLTAGE_CELL_52 0x1E73
|
||||
#define PID_CELLVOLTAGE_CELL_53 0x1E74
|
||||
#define PID_CELLVOLTAGE_CELL_54 0x1E75
|
||||
#define PID_CELLVOLTAGE_CELL_55 0x1E76
|
||||
#define PID_CELLVOLTAGE_CELL_56 0x1E77
|
||||
#define PID_CELLVOLTAGE_CELL_57 0x1E78
|
||||
#define PID_CELLVOLTAGE_CELL_58 0x1E79
|
||||
#define PID_CELLVOLTAGE_CELL_59 0x1E7A
|
||||
#define PID_CELLVOLTAGE_CELL_60 0x1E7B
|
||||
#define PID_CELLVOLTAGE_CELL_61 0x1E7C
|
||||
#define PID_CELLVOLTAGE_CELL_62 0x1E7D
|
||||
#define PID_CELLVOLTAGE_CELL_63 0x1E7E
|
||||
#define PID_CELLVOLTAGE_CELL_64 0x1E7F
|
||||
#define PID_CELLVOLTAGE_CELL_65 0x1E80
|
||||
#define PID_CELLVOLTAGE_CELL_66 0x1E81
|
||||
#define PID_CELLVOLTAGE_CELL_67 0x1E82
|
||||
#define PID_CELLVOLTAGE_CELL_68 0x1E83
|
||||
#define PID_CELLVOLTAGE_CELL_69 0x1E84
|
||||
#define PID_CELLVOLTAGE_CELL_70 0x1E85
|
||||
#define PID_CELLVOLTAGE_CELL_71 0x1E86
|
||||
#define PID_CELLVOLTAGE_CELL_72 0x1E87
|
||||
#define PID_CELLVOLTAGE_CELL_73 0x1E88
|
||||
#define PID_CELLVOLTAGE_CELL_74 0x1E89
|
||||
#define PID_CELLVOLTAGE_CELL_75 0x1E8A
|
||||
#define PID_CELLVOLTAGE_CELL_76 0x1E8B
|
||||
#define PID_CELLVOLTAGE_CELL_77 0x1E8C
|
||||
#define PID_CELLVOLTAGE_CELL_78 0x1E8D
|
||||
#define PID_CELLVOLTAGE_CELL_79 0x1E8E
|
||||
#define PID_CELLVOLTAGE_CELL_80 0x1E8F
|
||||
#define PID_CELLVOLTAGE_CELL_81 0x1E90
|
||||
#define PID_CELLVOLTAGE_CELL_82 0x1E91
|
||||
#define PID_CELLVOLTAGE_CELL_83 0x1E92
|
||||
#define PID_CELLVOLTAGE_CELL_84 0x1E93
|
||||
#define PID_CELLVOLTAGE_CELL_85 0x1E94
|
||||
#define PID_CELLVOLTAGE_CELL_86 0x1E95
|
||||
#define PID_CELLVOLTAGE_CELL_87 0x1E96
|
||||
#define PID_CELLVOLTAGE_CELL_88 0x1E97
|
||||
#define PID_CELLVOLTAGE_CELL_89 0x1E98
|
||||
#define PID_CELLVOLTAGE_CELL_90 0x1E99
|
||||
#define PID_CELLVOLTAGE_CELL_91 0x1E9A
|
||||
#define PID_CELLVOLTAGE_CELL_92 0x1E9B
|
||||
#define PID_CELLVOLTAGE_CELL_93 0x1E9C
|
||||
#define PID_CELLVOLTAGE_CELL_94 0x1E9D
|
||||
#define PID_CELLVOLTAGE_CELL_95 0x1E9E
|
||||
#define PID_CELLVOLTAGE_CELL_96 0x1E9F
|
||||
#define PID_CELLVOLTAGE_CELL_97 0x1EA0
|
||||
#define PID_CELLVOLTAGE_CELL_98 0x1EA1
|
||||
#define PID_CELLVOLTAGE_CELL_99 0x1EA2
|
||||
#define PID_CELLVOLTAGE_CELL_100 0x1EA3
|
||||
#define PID_CELLVOLTAGE_CELL_101 0x1EA4
|
||||
#define PID_CELLVOLTAGE_CELL_102 0x1EA5
|
||||
#define PID_CELLVOLTAGE_CELL_103 0x1EA6
|
||||
#define PID_CELLVOLTAGE_CELL_104 0x1EA7
|
||||
#define PID_CELLVOLTAGE_CELL_105 0x1EA8
|
||||
#define PID_CELLVOLTAGE_CELL_106 0x1EA9
|
||||
#define PID_CELLVOLTAGE_CELL_107 0x1EAA
|
||||
#define PID_CELLVOLTAGE_CELL_108 0x1EAB
|
||||
|
||||
void setup_battery(void);
|
||||
void transmit_can(CAN_frame* tx_frame, int interface);
|
||||
|
||||
#endif
|
|
@ -42,10 +42,6 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.temperature_min_dC;
|
||||
|
||||
datalayer.battery.status.temperature_max_dC;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
|
|
|
@ -38,11 +38,13 @@ CAN_frame LEAF_1D4 = {.FD = false,
|
|||
.ID = 0x1D4,
|
||||
.data = {0x6E, 0x6E, 0x00, 0x04, 0x07, 0x46, 0xE0, 0x44}};
|
||||
// Active polling messages
|
||||
uint8_t PIDgroups[] = {0x01, 0x02, 0x04, 0x83, 0x84, 0x90};
|
||||
uint8_t PIDindex = 0;
|
||||
CAN_frame LEAF_GROUP_REQUEST = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x79B,
|
||||
.data = {2, 0x21, 1, 0, 0, 0, 0, 0}};
|
||||
.data = {0x02, 0x21, PIDgroups[0], 0, 0, 0, 0, 0}};
|
||||
CAN_frame LEAF_NEXT_LINE_REQUEST = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
@ -107,7 +109,6 @@ static bool battery_Batt_Heater_Mail_Send_Request = false; //Stores info when a
|
|||
// Nissan LEAF battery data from polled CAN messages
|
||||
static uint8_t battery_request_idx = 0;
|
||||
static uint8_t group_7bb = 0;
|
||||
static uint8_t group = 1;
|
||||
static bool stop_battery_query = true;
|
||||
static uint8_t hold_off_with_polling_10seconds = 10;
|
||||
static uint16_t battery_cell_voltages[97]; //array with all the cellvoltages
|
||||
|
@ -124,7 +125,9 @@ static uint16_t battery_temp_raw_max = 0;
|
|||
static uint16_t battery_temp_raw_min = 0;
|
||||
static int16_t battery_temp_polled_max = 0;
|
||||
static int16_t battery_temp_polled_min = 0;
|
||||
|
||||
static uint8_t BatterySerialNumber[15] = {0}; // Stores raw HEX values for ASCII chars
|
||||
static uint8_t BatteryPartNumber[7] = {0}; // Stores raw HEX values for ASCII chars
|
||||
static uint8_t BMSIDcode[8] = {0};
|
||||
#ifdef DOUBLE_BATTERY
|
||||
static uint8_t LEAF_battery2_Type = ZE0_BATTERY;
|
||||
static bool battery2_can_alive = false;
|
||||
|
@ -326,6 +329,9 @@ void update_values_battery() { /* This function maps all the values fetched via
|
|||
}
|
||||
|
||||
// Update webserver datalayer
|
||||
memcpy(datalayer_extended.nissanleaf.BatterySerialNumber, BatterySerialNumber, sizeof(BatterySerialNumber));
|
||||
memcpy(datalayer_extended.nissanleaf.BatteryPartNumber, BatteryPartNumber, sizeof(BatteryPartNumber));
|
||||
memcpy(datalayer_extended.nissanleaf.BMSIDcode, BMSIDcode, sizeof(BMSIDcode));
|
||||
datalayer_extended.nissanleaf.LEAF_gen = LEAF_battery_Type;
|
||||
datalayer_extended.nissanleaf.GIDS = battery_GIDS;
|
||||
datalayer_extended.nissanleaf.ChargePowerLimit = battery_Charge_Power_Limit;
|
||||
|
@ -598,20 +604,16 @@ void receive_can_battery2(CAN_frame rx_frame) {
|
|||
hold_off_with_polling_10seconds = 10; //Polling is paused for 100s
|
||||
break;
|
||||
case 0x7BB:
|
||||
//First check which group data we are getting
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First message of a group
|
||||
battery2_group_7bb = rx_frame.data.u8[3];
|
||||
if (battery2_group_7bb != 1 && battery2_group_7bb != 2 &&
|
||||
battery2_group_7bb != 4) { //We are only interested in groups 1,2 and 4
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop_battery_query) { //Leafspy/Service request is active, stop our own polling
|
||||
break;
|
||||
}
|
||||
|
||||
transmit_can(&LEAF_NEXT_LINE_REQUEST, can_config.battery_double);
|
||||
//First check which group data we are getting
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First message of a group
|
||||
battery2_group_7bb = rx_frame.data.u8[3];
|
||||
transmit_can(&LEAF_NEXT_LINE_REQUEST, can_config.battery_double);
|
||||
}
|
||||
|
||||
if (battery2_group_7bb == 1) //High precision SOC, Current, voltages etc.
|
||||
{
|
||||
|
@ -845,7 +847,7 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x7BB:
|
||||
|
||||
// This section checks if we are doing a SOH reset towards BMS
|
||||
// This section checks if we are doing a SOH reset towards BMS. If we do, all 7BB handling is halted
|
||||
if (stateMachineClearSOH < 255) {
|
||||
//Intercept the messages based on state machine
|
||||
if (rx_frame.data.u8[0] == 0x06) { // Incoming challenge data!
|
||||
|
@ -860,18 +862,16 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
break;
|
||||
}
|
||||
|
||||
//First check which group data we are getting
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First message of a group
|
||||
group_7bb = rx_frame.data.u8[3];
|
||||
if (group_7bb != 1 && group_7bb != 2 && group_7bb != 4) { //We are only interested in groups 1,2 and 4
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop_battery_query) { //Leafspy is active, stop our own polling
|
||||
break;
|
||||
}
|
||||
transmit_can(&LEAF_NEXT_LINE_REQUEST, can_config.battery); //Request the next frame for the group
|
||||
|
||||
//First check which group data we are getting
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First message of a group
|
||||
group_7bb = rx_frame.data.u8[3];
|
||||
|
||||
transmit_can(&LEAF_NEXT_LINE_REQUEST, can_config.battery); //Request the next frame for the group
|
||||
}
|
||||
|
||||
if (group_7bb == 1) //High precision SOC, Current, voltages etc.
|
||||
{
|
||||
|
@ -991,6 +991,66 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
}
|
||||
}
|
||||
|
||||
if (group_7bb == 0x83) //BatteryPartNumber
|
||||
{
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First frame (101A6183334E4B32)
|
||||
BatteryPartNumber[0] = rx_frame.data.u8[4];
|
||||
BatteryPartNumber[1] = rx_frame.data.u8[5];
|
||||
BatteryPartNumber[2] = rx_frame.data.u8[6];
|
||||
BatteryPartNumber[3] = rx_frame.data.u8[7];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x21) { //Second frame (2141524205170000)
|
||||
BatteryPartNumber[4] = rx_frame.data.u8[1];
|
||||
BatteryPartNumber[5] = rx_frame.data.u8[2];
|
||||
BatteryPartNumber[6] = rx_frame.data.u8[3];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x22) { //Third frame (2200000002101311)
|
||||
}
|
||||
|
||||
if (rx_frame.data.u8[0] == 0x23) { //Fourth frame (23000000000080FF)
|
||||
}
|
||||
}
|
||||
if (group_7bb == 0x84) { //BatterySerialNumber
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First frame (10 16 61 84 32 33 30 55)
|
||||
BatterySerialNumber[0] = rx_frame.data.u8[7];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x21) { //Second frame (21 4B 31 31 39 32 45 30)
|
||||
BatterySerialNumber[1] = rx_frame.data.u8[1];
|
||||
BatterySerialNumber[2] = rx_frame.data.u8[2];
|
||||
BatterySerialNumber[3] = rx_frame.data.u8[3];
|
||||
BatterySerialNumber[4] = rx_frame.data.u8[4];
|
||||
BatterySerialNumber[5] = rx_frame.data.u8[5];
|
||||
BatterySerialNumber[6] = rx_frame.data.u8[6];
|
||||
BatterySerialNumber[7] = rx_frame.data.u8[7];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x22) { //Third frame (22 30 31 34 38 32 20 A0)
|
||||
BatterySerialNumber[8] = rx_frame.data.u8[1];
|
||||
BatterySerialNumber[9] = rx_frame.data.u8[2];
|
||||
BatterySerialNumber[10] = rx_frame.data.u8[3];
|
||||
BatterySerialNumber[11] = rx_frame.data.u8[4];
|
||||
BatterySerialNumber[12] = rx_frame.data.u8[5];
|
||||
BatterySerialNumber[13] = rx_frame.data.u8[6];
|
||||
BatterySerialNumber[14] = rx_frame.data.u8[7];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x23) { //Fourth frame (23 00 00 00 00 00 00 00)
|
||||
}
|
||||
}
|
||||
|
||||
if (group_7bb == 0x90) { //BMSIDcode
|
||||
if (rx_frame.data.u8[0] == 0x10) { //First frame (100A619044434131)
|
||||
BMSIDcode[0] = rx_frame.data.u8[4];
|
||||
BMSIDcode[1] = rx_frame.data.u8[5];
|
||||
BMSIDcode[2] = rx_frame.data.u8[6];
|
||||
BMSIDcode[3] = rx_frame.data.u8[7];
|
||||
}
|
||||
if (rx_frame.data.u8[0] == 0x21) { //Second frame (2130303535FFFFFF)
|
||||
BMSIDcode[4] = rx_frame.data.u8[1];
|
||||
BMSIDcode[5] = rx_frame.data.u8[2];
|
||||
BMSIDcode[6] = rx_frame.data.u8[3];
|
||||
BMSIDcode[7] = rx_frame.data.u8[4];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1190,9 +1250,11 @@ void send_can_battery() {
|
|||
|
||||
//Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?)
|
||||
if (!stop_battery_query) {
|
||||
group = (group == 1) ? 2 : (group == 2) ? 4 : 1;
|
||||
// Cycle between group 1, 2, and 4 using ternary operation
|
||||
LEAF_GROUP_REQUEST.data.u8[2] = group;
|
||||
|
||||
// Move to the next group
|
||||
PIDindex = (PIDindex + 1) % 6; // 6 = amount of elements in the PIDgroups[]
|
||||
LEAF_GROUP_REQUEST.data.u8[2] = PIDgroups[PIDindex];
|
||||
|
||||
transmit_can(&LEAF_GROUP_REQUEST, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can(&LEAF_GROUP_REQUEST, can_config.battery_double);
|
||||
|
|
|
@ -103,36 +103,36 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
|
||||
datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Values going to inverter:");
|
||||
Serial.print("SOH%: ");
|
||||
Serial.print(datalayer.battery.status.soh_pptt);
|
||||
Serial.print(", SOC% scaled: ");
|
||||
Serial.print(datalayer.battery.status.reported_soc);
|
||||
Serial.print(", Voltage: ");
|
||||
Serial.print(datalayer.battery.status.voltage_dV);
|
||||
Serial.print(", Max discharge power: ");
|
||||
Serial.print(datalayer.battery.status.max_discharge_power_W);
|
||||
Serial.print(", Max charge power: ");
|
||||
Serial.print(datalayer.battery.status.max_charge_power_W);
|
||||
Serial.print(", Max temp: ");
|
||||
Serial.print(datalayer.battery.status.temperature_max_dC);
|
||||
Serial.print(", Min temp: ");
|
||||
Serial.print(datalayer.battery.status.temperature_min_dC);
|
||||
Serial.print(", BMS Status (3=OK): ");
|
||||
Serial.print(datalayer.battery.status.bms_status);
|
||||
#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);
|
||||
|
||||
Serial.println("Battery values: ");
|
||||
Serial.print("Real SOC: ");
|
||||
Serial.print(LB_SOC);
|
||||
Serial.print(", Current: ");
|
||||
Serial.print(LB_Current);
|
||||
Serial.print(", kWh remain: ");
|
||||
Serial.print(LB_kWh_Remaining);
|
||||
Serial.print(", max mV: ");
|
||||
Serial.print(LB_Cell_Max_Voltage);
|
||||
Serial.print(", min mV: ");
|
||||
Serial.print(LB_Cell_Min_Voltage);
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "../include.h"
|
||||
#ifdef RENAULT_ZOE_GEN1_BATTERY
|
||||
#include <algorithm> // For std::min and std::max
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "RENAULT-ZOE-GEN1-BATTERY.h"
|
||||
|
@ -9,20 +8,24 @@
|
|||
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
|
||||
Still TODO:
|
||||
- Find max discharge and max charge values (for now hardcoded to 5kW)
|
||||
- Fix the missing cell96 issue (Only cells 1-95 is shown)
|
||||
- Find current sensor value (OVMS code reads this from inverter, which we dont have)
|
||||
- Figure out why SOH% is not read (low prio)
|
||||
- Fix the missing cell96 issue (Only cells 1-95 is shown on cellmonitor page)
|
||||
- Automatically detect if we are on 22 or 41kWh battery (Nice to have, requires log file from 22kWh battery)
|
||||
/*
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
static uint16_t LB_SOC = 50;
|
||||
static uint16_t LB_Display_SOC = 50;
|
||||
static uint16_t LB_SOH = 99;
|
||||
static int16_t LB_Average_Temperature = 0;
|
||||
static uint32_t LB_Charge_Power_W = 0;
|
||||
static int32_t LB_Current = 0;
|
||||
static uint32_t LB_Charging_Power_W = 0;
|
||||
static uint32_t LB_Regen_allowed_W = 0;
|
||||
static uint32_t LB_Discharge_allowed_W = 0;
|
||||
static int16_t LB_Current = 0;
|
||||
static int16_t LB_Cell_minimum_temperature = 0;
|
||||
static int16_t LB_Cell_maximum_temperature = 0;
|
||||
static uint16_t LB_kWh_Remaining = 0;
|
||||
static uint16_t LB_Battery_Voltage = 3700;
|
||||
static uint8_t LB_Heartbeat = 0;
|
||||
static uint8_t frame0 = 0;
|
||||
static uint8_t current_poll = 0;
|
||||
static uint8_t requested_poll = 0;
|
||||
|
@ -77,32 +80,17 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.soh_pptt = (LB_SOH * 100); // Increase range from 99% -> 99.00%
|
||||
|
||||
datalayer.battery.status.real_soc = SOC_polled;
|
||||
//datalayer.battery.status.real_soc = LB_Display_SOC; //Alternative would be to use Dash SOC%
|
||||
|
||||
datalayer.battery.status.current_dA = LB_Current; //TODO: Take from CAN
|
||||
datalayer.battery.status.current_dA = LB_Current * 10; //Convert A to dA
|
||||
|
||||
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
||||
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 = 5000; //TODO: Take from CAN
|
||||
datalayer.battery.status.max_discharge_power_W = LB_Discharge_allowed_W;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = 5000; //TODO: Take from CAN
|
||||
// TODO: Remove this hacky wacky scaling down charge power when we find value from CAN
|
||||
if (datalayer.battery.status.real_soc > 9500) {
|
||||
datalayer.battery.status.max_charge_power_W = 3000;
|
||||
}
|
||||
if (datalayer.battery.status.real_soc > 9600) {
|
||||
datalayer.battery.status.max_charge_power_W = 2000;
|
||||
}
|
||||
if (datalayer.battery.status.real_soc > 9700) {
|
||||
datalayer.battery.status.max_charge_power_W = 1000;
|
||||
}
|
||||
if (datalayer.battery.status.real_soc > 9800) {
|
||||
datalayer.battery.status.max_charge_power_W = 500;
|
||||
}
|
||||
if (datalayer.battery.status.real_soc > 9900) {
|
||||
datalayer.battery.status.max_charge_power_W = 50;
|
||||
}
|
||||
datalayer.battery.status.max_charge_power_W = LB_Regen_allowed_W;
|
||||
|
||||
int16_t temperatures[] = {cell_1_temperature_polled, cell_2_temperature_polled, cell_3_temperature_polled,
|
||||
cell_4_temperature_polled, cell_5_temperature_polled, cell_6_temperature_polled,
|
||||
|
@ -145,28 +133,61 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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.voltage_dV = static_cast<uint32_t>((calculated_total_pack_voltage_mV / 100)); // mV to dV
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x427:
|
||||
LB_Charge_Power_W = rx_frame.data.u8[5] * 300;
|
||||
case 0x155: //10ms - Charging power, current and SOC
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
LB_Charging_Power_W = rx_frame.data.u8[0] * 300;
|
||||
LB_Current = (((((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]) * 0.25) - 500);
|
||||
LB_Display_SOC = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
break;
|
||||
case 0x427: // NOTE: Not present on 41kWh battery!
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
LB_kWh_Remaining = (((((rx_frame.data.u8[6] << 8) | (rx_frame.data.u8[7])) >> 6) & 0x3ff) * 0.1);
|
||||
break;
|
||||
case 0x42E: //HV SOC & Battery Temp & Charging Power
|
||||
case 0x42E: //NOTE: Not present on 41kWh battery!
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
LB_Battery_Voltage = (((((rx_frame.data.u8[3] << 8) | (rx_frame.data.u8[4])) >> 5) & 0x3ff) * 0.5); //0.5V/bit
|
||||
LB_Average_Temperature = (((((rx_frame.data.u8[5] << 8) | (rx_frame.data.u8[6])) >> 5) & 0x7F) - 40);
|
||||
break;
|
||||
case 0x424: //100ms - Charge limits, Temperatures, SOH
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
LB_Regen_allowed_W = rx_frame.data.u8[2] * 500;
|
||||
LB_Discharge_allowed_W = rx_frame.data.u8[3] * 500;
|
||||
LB_Cell_minimum_temperature = (rx_frame.data.u8[4] - 40);
|
||||
LB_SOH = rx_frame.data.u8[5];
|
||||
LB_Heartbeat = rx_frame.data.u8[6]; // Alternates between 0x55 and 0xAA every 500ms (Same as on Nissan LEAF)
|
||||
LB_Cell_maximum_temperature = (rx_frame.data.u8[7] - 40);
|
||||
break;
|
||||
case 0x425: //100ms Unknown content
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//Sent only? by 41kWh battery
|
||||
break;
|
||||
case 0x445: //100ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//Sent only? by 41kWh battery (potential use for detecting which generation we are on)
|
||||
break;
|
||||
case 0x4AE: //3000ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//Sent only? by 41kWh battery (potential use for detecting which generation we are on)
|
||||
break;
|
||||
case 0x4AF: //100ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//Sent only? by 41kWh battery (potential use for detecting which generation we are on)
|
||||
break;
|
||||
case 0x654: //SOC
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
LB_SOC = rx_frame.data.u8[3];
|
||||
break;
|
||||
case 0x658: //SOH
|
||||
LB_SOH = (rx_frame.data.u8[4] & 0x7F);
|
||||
case 0x658: //SOH - NOTE: Not present on 41kWh battery! (Is this message on 21kWh?)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//LB_SOH = (rx_frame.data.u8[4] & 0x7F);
|
||||
break;
|
||||
case 0x659: //3000ms
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
//Sent only? by 41kWh battery (potential use for detecting which generation we are on)
|
||||
break;
|
||||
case 0x7BB: //Reply from active polling
|
||||
frame0 = rx_frame.data.u8[0];
|
||||
|
|
|
@ -216,10 +216,6 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer_extended.zoePH2.battery_pack_time = battery_pack_time;
|
||||
datalayer_extended.zoePH2.battery_soc_min = battery_soc_min;
|
||||
datalayer_extended.zoePH2.battery_soc_max = battery_soc_max;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
|
|
|
@ -162,17 +162,17 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
|
||||
/*
|
||||
// All CAN messages recieved will be logged via serial
|
||||
Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.ID, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(rx_frame.DLC);
|
||||
Serial.print(" ");
|
||||
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.ID, HEX);
|
||||
logging.print(" ");
|
||||
logging.print(rx_frame.DLC);
|
||||
logging.print(" ");
|
||||
for (int i = 0; i < rx_frame.DLC; ++i) {
|
||||
Serial.print(rx_frame.data.u8[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(rx_frame.data.u8[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
*/
|
||||
switch (rx_frame.ID) {
|
||||
case 0xF5: // This is the only message is sent from BMS
|
||||
|
|
|
@ -18,6 +18,8 @@ static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN
|
|||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
|
||||
static uint8_t poll_data_pid = 0;
|
||||
static uint8_t counter_200 = 0;
|
||||
static uint8_t checksum_200 = 0;
|
||||
|
||||
static uint16_t SOC_Display = 0;
|
||||
static uint16_t batterySOH = 100;
|
||||
|
@ -32,11 +34,27 @@ static int16_t leadAcidBatteryVoltage = 120;
|
|||
static int8_t temperatureMax = 0;
|
||||
static int8_t temperatureMin = 0;
|
||||
static int16_t batteryAmps = 0;
|
||||
static uint8_t counter_200 = 0;
|
||||
static uint8_t checksum_200 = 0;
|
||||
static uint8_t StatusBattery = 0;
|
||||
static uint16_t cellvoltages_mv[96];
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
static uint16_t battery2_SOC_Display = 0;
|
||||
static uint16_t battery2_SOH = 100;
|
||||
static uint16_t battery2_CellVoltMax_mV = 3700;
|
||||
static uint16_t battery2_CellVoltMin_mV = 3700;
|
||||
static uint8_t battery2_CellVmaxNo = 0;
|
||||
static uint8_t battery2_CellVminNo = 0;
|
||||
static uint16_t battery2_allowedDischargePower = 0;
|
||||
static uint16_t battery2_allowedChargePower = 0;
|
||||
static uint16_t battery2_batteryVoltage = 0;
|
||||
static int16_t battery2_leadAcidBatteryVoltage = 120;
|
||||
static int8_t battery2_temperatureMax = 0;
|
||||
static int8_t battery2_temperatureMin = 0;
|
||||
static int16_t battery2_batteryAmps = 0;
|
||||
static uint8_t battery2_StatusBattery = 0;
|
||||
static uint16_t battery2_cellvoltages_mv[96];
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
||||
CAN_frame SANTAFE_200 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
@ -96,10 +114,6 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
if (leadAcidBatteryVoltage < 110) {
|
||||
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
|
@ -338,10 +352,13 @@ void send_can_battery() {
|
|||
SANTAFE_200.data.u8[7] = checksum_200;
|
||||
|
||||
transmit_can(&SANTAFE_200, can_config.battery);
|
||||
|
||||
transmit_can(&SANTAFE_2A1, can_config.battery);
|
||||
|
||||
transmit_can(&SANTAFE_2F0, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can(&SANTAFE_200, can_config.battery_double);
|
||||
transmit_can(&SANTAFE_2A1, can_config.battery_double);
|
||||
transmit_can(&SANTAFE_2F0, can_config.battery_double);
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
||||
counter_200++;
|
||||
if (counter_200 > 0xF) {
|
||||
|
@ -354,36 +371,279 @@ void send_can_battery() {
|
|||
previousMillis100 = currentMillis;
|
||||
|
||||
transmit_can(&SANTAFE_523, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can(&SANTAFE_523, can_config.battery_double);
|
||||
#endif //DOUBLE_BATTERY
|
||||
}
|
||||
|
||||
// Send 500ms CAN Message
|
||||
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
||||
previousMillis500 = currentMillis;
|
||||
|
||||
//PID data is polled after last message sent from battery:
|
||||
if (poll_data_pid >= 5) { //polling one of 5 PIDs at 100ms, resolution = 500ms
|
||||
poll_data_pid = 0;
|
||||
}
|
||||
poll_data_pid++;
|
||||
if (poll_data_pid == 1) {
|
||||
SANTAFE_7E4_poll.data.u8[3] = 0x01;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
} else if (poll_data_pid == 2) {
|
||||
SANTAFE_7E4_poll.data.u8[3] = 0x02;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
} else if (poll_data_pid == 3) {
|
||||
SANTAFE_7E4_poll.data.u8[3] = 0x03;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
} else if (poll_data_pid == 4) {
|
||||
SANTAFE_7E4_poll.data.u8[3] = 0x04;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
} else if (poll_data_pid == 5) {
|
||||
SANTAFE_7E4_poll.data.u8[3] = 0x05;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
}
|
||||
// PID data is polled after last message sent from battery:
|
||||
poll_data_pid = (poll_data_pid % 5) + 1;
|
||||
SANTAFE_7E4_poll.data.u8[3] = (uint8_t)poll_data_pid;
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can(&SANTAFE_7E4_poll, can_config.battery_double);
|
||||
#endif //DOUBLE_BATTERY
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
void update_values_battery2() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
datalayer.battery2.status.real_soc = (battery2_SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||
|
||||
datalayer.battery2.status.soh_pptt = (battery2_SOH * 100); //Increase decimals from 100% -> 100.00%
|
||||
|
||||
datalayer.battery2.status.voltage_dV = battery2_batteryVoltage;
|
||||
|
||||
datalayer.battery2.status.current_dA = -battery2_batteryAmps;
|
||||
|
||||
datalayer.battery2.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||
(static_cast<double>(datalayer.battery2.status.real_soc) / 10000) * datalayer.battery2.info.total_capacity_Wh);
|
||||
|
||||
datalayer.battery2.status.max_discharge_power_W = battery2_allowedDischargePower * 10;
|
||||
|
||||
datalayer.battery2.status.max_charge_power_W = battery2_allowedChargePower * 10;
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery2.status.active_power_W =
|
||||
((datalayer.battery2.status.voltage_dV * datalayer.battery2.status.current_dA) / 100);
|
||||
|
||||
datalayer.battery2.status.cell_max_voltage_mV = battery2_CellVoltMax_mV;
|
||||
|
||||
datalayer.battery2.status.cell_min_voltage_mV = battery2_CellVoltMin_mV;
|
||||
|
||||
datalayer.battery2.status.temperature_min_dC = battery2_temperatureMin * 10; //Increase decimals, 17C -> 17.0C
|
||||
|
||||
datalayer.battery2.status.temperature_max_dC = battery2_temperatureMax * 10; //Increase decimals, 18C -> 18.0C
|
||||
|
||||
if (battery2_leadAcidBatteryVoltage < 110) {
|
||||
set_event(EVENT_12V_LOW, battery2_leadAcidBatteryVoltage);
|
||||
}
|
||||
}
|
||||
|
||||
void receive_can_battery2(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x1FF:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_StatusBattery = (rx_frame.data.u8[0] & 0x0F);
|
||||
break;
|
||||
case 0x4D5:
|
||||
break;
|
||||
case 0x4DD:
|
||||
break;
|
||||
case 0x4DE:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x4E0:
|
||||
break;
|
||||
case 0x542:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_SOC_Display = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]) / 2;
|
||||
break;
|
||||
case 0x588:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_batteryVoltage = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]);
|
||||
break;
|
||||
case 0x597:
|
||||
break;
|
||||
case 0x5A6:
|
||||
break;
|
||||
case 0x5A7:
|
||||
break;
|
||||
case 0x5AD:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_batteryAmps = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2];
|
||||
break;
|
||||
case 0x5AE:
|
||||
break;
|
||||
case 0x5F1:
|
||||
break;
|
||||
case 0x620:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_leadAcidBatteryVoltage = rx_frame.data.u8[1];
|
||||
battery2_temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery
|
||||
battery2_temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery
|
||||
break;
|
||||
case 0x670:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery2_allowedChargePower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||
battery2_allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||
break;
|
||||
case 0x671:
|
||||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x7EC: //Data From polled PID group, BigEndian
|
||||
switch (rx_frame.data.u8[0]) {
|
||||
case 0x10: //"PID Header"
|
||||
if (rx_frame.data.u8[4] == poll_data_pid) {
|
||||
transmit_can(&SANTAFE_7E4_ack,
|
||||
can_config.battery_double); //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) {
|
||||
} else if (poll_data_pid == 2) {
|
||||
battery2_cellvoltages_mv[0] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[1] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[2] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[3] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[4] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[5] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
battery2_cellvoltages_mv[32] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[33] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[34] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[35] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[36] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[37] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 4) {
|
||||
battery2_cellvoltages_mv[64] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[65] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[66] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[67] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[68] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[69] = (rx_frame.data.u8[7] * 20);
|
||||
}
|
||||
break;
|
||||
case 0x22: //Second datarow in PID group
|
||||
if (poll_data_pid == 2) {
|
||||
battery2_cellvoltages_mv[6] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[7] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[8] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[9] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[10] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[11] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[12] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
battery2_cellvoltages_mv[38] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[39] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[40] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[41] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[42] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[43] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[44] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 4) {
|
||||
battery2_cellvoltages_mv[70] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[71] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[72] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[73] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[74] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[75] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[76] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 6) {
|
||||
}
|
||||
break;
|
||||
case 0x23: //Third datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
battery2_CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
|
||||
} else if (poll_data_pid == 2) {
|
||||
battery2_cellvoltages_mv[13] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[14] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[15] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[16] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[17] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[18] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[19] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
battery2_cellvoltages_mv[45] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[46] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[47] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[48] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[49] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[50] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[51] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 4) {
|
||||
battery2_cellvoltages_mv[77] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[78] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[79] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[80] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[81] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[82] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[83] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 5) {
|
||||
if (rx_frame.data.u8[6] > 0) {
|
||||
battery2_SOH = rx_frame.data.u8[6];
|
||||
}
|
||||
if (battery2_SOH > 100) {
|
||||
battery2_SOH = 100;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x24: //Fourth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
battery2_CellVmaxNo = rx_frame.data.u8[1];
|
||||
battery2_CellVminNo = rx_frame.data.u8[3];
|
||||
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
|
||||
} else if (poll_data_pid == 2) {
|
||||
battery2_cellvoltages_mv[20] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[21] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[22] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[23] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[24] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[25] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[26] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
battery2_cellvoltages_mv[52] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[53] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[54] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[55] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[56] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[57] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[58] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 4) {
|
||||
battery2_cellvoltages_mv[84] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[85] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[86] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[87] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[88] = (rx_frame.data.u8[5] * 20);
|
||||
battery2_cellvoltages_mv[89] = (rx_frame.data.u8[6] * 20);
|
||||
battery2_cellvoltages_mv[90] = (rx_frame.data.u8[7] * 20);
|
||||
} else if (poll_data_pid == 5) {
|
||||
}
|
||||
break;
|
||||
case 0x25: //Fifth datarow in PID group
|
||||
if (poll_data_pid == 2) {
|
||||
battery2_cellvoltages_mv[27] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[28] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[29] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[30] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[31] = (rx_frame.data.u8[5] * 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
battery2_cellvoltages_mv[59] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[60] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[61] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[62] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[63] = (rx_frame.data.u8[5] * 20);
|
||||
} else if (poll_data_pid == 4) {
|
||||
battery2_cellvoltages_mv[91] = (rx_frame.data.u8[1] * 20);
|
||||
battery2_cellvoltages_mv[92] = (rx_frame.data.u8[2] * 20);
|
||||
battery2_cellvoltages_mv[93] = (rx_frame.data.u8[3] * 20);
|
||||
battery2_cellvoltages_mv[94] = (rx_frame.data.u8[4] * 20);
|
||||
battery2_cellvoltages_mv[95] = (rx_frame.data.u8[5] * 20);
|
||||
|
||||
//Map all cell voltages to the global array, we have sampled them all!
|
||||
memcpy(datalayer.battery2.status.cell_voltages_mV, battery2_cellvoltages_mv, 96 * sizeof(uint16_t));
|
||||
} else if (poll_data_pid == 5) {
|
||||
}
|
||||
break;
|
||||
case 0x26: //Sixth datarow in PID group
|
||||
break;
|
||||
case 0x27: //Seventh datarow in PID group
|
||||
break;
|
||||
case 0x28: //Eighth datarow in PID group
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
||||
uint8_t CalculateCRC8(CAN_frame rx_frame) {
|
||||
int crc = 0;
|
||||
|
||||
|
@ -409,6 +669,16 @@ void setup_battery(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.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells;
|
||||
datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
|
||||
datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
|
||||
datalayer.battery2.info.max_cell_voltage_mV = datalayer.battery.info.max_cell_voltage_mV;
|
||||
datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV;
|
||||
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
|
||||
#endif //DOUBLE_BATTERY
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -94,8 +94,8 @@ void manageSerialLinkReceiver() {
|
|||
bool readError = dataLinkReceive.checkReadError(true); // check for error & clear error flag
|
||||
|
||||
if (readError) {
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - ERROR: SerialDataLink - Read Error");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - ERROR: SerialDataLink - Read Error");
|
||||
lasterror = true;
|
||||
errors++;
|
||||
}
|
||||
|
@ -112,8 +112,8 @@ void manageSerialLinkReceiver() {
|
|||
//bms_status = ACTIVE; // just testing
|
||||
if (lasterror) {
|
||||
lasterror = false;
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - RECOVERY: SerialDataLink - Read GOOD");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - RECOVERY: SerialDataLink - Read GOOD");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,34 +134,34 @@ void manageSerialLinkReceiver() {
|
|||
// report Lost data & Max charge / Discharge reductions
|
||||
if (minutesLost != last_minutesLost) {
|
||||
last_minutesLost = minutesLost;
|
||||
Serial.print(currentTime);
|
||||
logging.print(currentTime);
|
||||
if (batteryFault) {
|
||||
Serial.print("Battery Fault (minutes) : ");
|
||||
logging.print("Battery Fault (minutes) : ");
|
||||
} else {
|
||||
Serial.print(" - Minutes without data : ");
|
||||
logging.print(" - Minutes without data : ");
|
||||
}
|
||||
Serial.print(minutesLost);
|
||||
Serial.print(", max Charge = ");
|
||||
Serial.print(datalayer.battery.status.max_charge_power_W);
|
||||
Serial.print(", max Discharge = ");
|
||||
Serial.println(datalayer.battery.status.max_discharge_power_W);
|
||||
logging.print(minutesLost);
|
||||
logging.print(", max Charge = ");
|
||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
||||
logging.print(", max Discharge = ");
|
||||
logging.println(datalayer.battery.status.max_discharge_power_W);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTime - reportTime > 59999) {
|
||||
reportTime = currentTime;
|
||||
Serial.print(currentTime);
|
||||
Serial.print(" SerialDataLink-Receiver - NewData :");
|
||||
Serial.print(reads);
|
||||
Serial.print(" Errors : ");
|
||||
Serial.println(errors);
|
||||
logging.print(currentTime);
|
||||
logging.print(" SerialDataLink-Receiver - NewData :");
|
||||
logging.print(reads);
|
||||
logging.print(" Errors : ");
|
||||
logging.println(errors);
|
||||
reads = 0;
|
||||
errors = 0;
|
||||
|
||||
// --- printUsefullData();
|
||||
//Serial.print("SOC = ");
|
||||
//Serial.println(SOC);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
//logging.print("SOC = ");
|
||||
//logging.println(SOC);
|
||||
#ifdef DEBUG_LOG
|
||||
update_values_serial_link();
|
||||
#endif
|
||||
}
|
||||
|
@ -179,43 +179,43 @@ void manageSerialLinkReceiver() {
|
|||
}
|
||||
|
||||
void update_values_serial_link() {
|
||||
Serial.println("Values from battery: ");
|
||||
Serial.print("SOC: ");
|
||||
Serial.print(datalayer.battery.status.real_soc);
|
||||
Serial.print(" SOH: ");
|
||||
Serial.print(datalayer.battery.status.soh_pptt);
|
||||
Serial.print(" Voltage: ");
|
||||
Serial.print(datalayer.battery.status.voltage_dV);
|
||||
Serial.print(" Current: ");
|
||||
Serial.print(datalayer.battery.status.current_dA);
|
||||
Serial.print(" Capacity: ");
|
||||
Serial.print(datalayer.battery.info.total_capacity_Wh);
|
||||
Serial.print(" Remain cap: ");
|
||||
Serial.print(datalayer.battery.status.remaining_capacity_Wh);
|
||||
Serial.print(" Max discharge W: ");
|
||||
Serial.print(datalayer.battery.status.max_discharge_power_W);
|
||||
Serial.print(" Max charge W: ");
|
||||
Serial.print(datalayer.battery.status.max_charge_power_W);
|
||||
Serial.print(" BMS status: ");
|
||||
Serial.print(datalayer.battery.status.bms_status);
|
||||
Serial.print(" Power: ");
|
||||
Serial.print(datalayer.battery.status.active_power_W);
|
||||
Serial.print(" Temp min: ");
|
||||
Serial.print(datalayer.battery.status.temperature_min_dC);
|
||||
Serial.print(" Temp max: ");
|
||||
Serial.print(datalayer.battery.status.temperature_max_dC);
|
||||
Serial.print(" Cell max: ");
|
||||
Serial.print(datalayer.battery.status.cell_max_voltage_mV);
|
||||
Serial.print(" Cell min: ");
|
||||
Serial.print(datalayer.battery.status.cell_min_voltage_mV);
|
||||
Serial.print(" LFP : ");
|
||||
Serial.print(datalayer.battery.info.chemistry);
|
||||
Serial.print(" Battery Allows Contactor Closing: ");
|
||||
Serial.print(datalayer.system.status.battery_allows_contactor_closing);
|
||||
Serial.print(" Inverter Allows Contactor Closing: ");
|
||||
Serial.print(datalayer.system.status.inverter_allows_contactor_closing);
|
||||
logging.println("Values from battery: ");
|
||||
logging.print("SOC: ");
|
||||
logging.print(datalayer.battery.status.real_soc);
|
||||
logging.print(" SOH: ");
|
||||
logging.print(datalayer.battery.status.soh_pptt);
|
||||
logging.print(" Voltage: ");
|
||||
logging.print(datalayer.battery.status.voltage_dV);
|
||||
logging.print(" Current: ");
|
||||
logging.print(datalayer.battery.status.current_dA);
|
||||
logging.print(" Capacity: ");
|
||||
logging.print(datalayer.battery.info.total_capacity_Wh);
|
||||
logging.print(" Remain cap: ");
|
||||
logging.print(datalayer.battery.status.remaining_capacity_Wh);
|
||||
logging.print(" Max discharge W: ");
|
||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
||||
logging.print(" Max charge W: ");
|
||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
||||
logging.print(" BMS status: ");
|
||||
logging.print(datalayer.battery.status.bms_status);
|
||||
logging.print(" Power: ");
|
||||
logging.print(datalayer.battery.status.active_power_W);
|
||||
logging.print(" Temp min: ");
|
||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
||||
logging.print(" Temp max: ");
|
||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
||||
logging.print(" Cell max: ");
|
||||
logging.print(datalayer.battery.status.cell_max_voltage_mV);
|
||||
logging.print(" Cell min: ");
|
||||
logging.print(datalayer.battery.status.cell_min_voltage_mV);
|
||||
logging.print(" LFP : ");
|
||||
logging.print(datalayer.battery.info.chemistry);
|
||||
logging.print(" Battery Allows Contactor Closing: ");
|
||||
logging.print(datalayer.system.status.battery_allows_contactor_closing);
|
||||
logging.print(" Inverter Allows Contactor Closing: ");
|
||||
logging.print(datalayer.system.status.inverter_allows_contactor_closing);
|
||||
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
}
|
||||
|
||||
void setup_battery(void) {
|
||||
|
|
|
@ -408,69 +408,69 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer_extended.tesla.battery_soc_min = battery_soc_min;
|
||||
datalayer_extended.tesla.battery_soc_ui = battery_soc_ui;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
printFaultCodesIfActive();
|
||||
|
||||
Serial.print("STATUS: Contactor: ");
|
||||
Serial.print(contactorText[battery_contactor]); //Display what state the contactor is in
|
||||
Serial.print(", HVIL: ");
|
||||
Serial.print(hvilStatusState[battery_hvil_status]);
|
||||
Serial.print(", NegativeState: ");
|
||||
Serial.print(contactorState[battery_packContNegativeState]);
|
||||
Serial.print(", PositiveState: ");
|
||||
Serial.print(contactorState[battery_packContPositiveState]);
|
||||
Serial.print(", setState: ");
|
||||
Serial.print(contactorState[battery_packContactorSetState]);
|
||||
Serial.print(", close allowed: ");
|
||||
Serial.print(battery_packCtrsClosingAllowed);
|
||||
Serial.print(", Pyrotest: ");
|
||||
Serial.println(battery_pyroTestInProgress);
|
||||
logging.print("STATUS: Contactor: ");
|
||||
logging.print(contactorText[battery_contactor]); //Display what state the contactor is in
|
||||
logging.print(", HVIL: ");
|
||||
logging.print(hvilStatusState[battery_hvil_status]);
|
||||
logging.print(", NegativeState: ");
|
||||
logging.print(contactorState[battery_packContNegativeState]);
|
||||
logging.print(", PositiveState: ");
|
||||
logging.print(contactorState[battery_packContPositiveState]);
|
||||
logging.print(", setState: ");
|
||||
logging.print(contactorState[battery_packContactorSetState]);
|
||||
logging.print(", close allowed: ");
|
||||
logging.print(battery_packCtrsClosingAllowed);
|
||||
logging.print(", Pyrotest: ");
|
||||
logging.println(battery_pyroTestInProgress);
|
||||
|
||||
Serial.print("Battery values: ");
|
||||
Serial.print("Real SOC: ");
|
||||
Serial.print(battery_soc_ui / 10.0, 1);
|
||||
logging.print("Battery values: ");
|
||||
logging.print("Real SOC: ");
|
||||
logging.print(battery_soc_ui / 10.0, 1);
|
||||
print_int_with_units(", Battery voltage: ", battery_volts, "V");
|
||||
print_int_with_units(", Battery HV current: ", (battery_amps * 0.1), "A");
|
||||
Serial.print(", Fully charged?: ");
|
||||
logging.print(", Fully charged?: ");
|
||||
if (battery_full_charge_complete)
|
||||
Serial.print("YES, ");
|
||||
logging.print("YES, ");
|
||||
else
|
||||
Serial.print("NO, ");
|
||||
logging.print("NO, ");
|
||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||
Serial.print("LFP chemistry detected!");
|
||||
logging.print("LFP chemistry detected!");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Cellstats, Max: ");
|
||||
Serial.print(battery_cell_max_v);
|
||||
Serial.print("mV (cell ");
|
||||
Serial.print(battery_max_vno);
|
||||
Serial.print("), Min: ");
|
||||
Serial.print(battery_cell_min_v);
|
||||
Serial.print("mV (cell ");
|
||||
Serial.print(battery_min_vno);
|
||||
Serial.print("), Imbalance: ");
|
||||
Serial.print(battery_cell_deviation_mV);
|
||||
Serial.println("mV.");
|
||||
logging.println("");
|
||||
logging.print("Cellstats, Max: ");
|
||||
logging.print(battery_cell_max_v);
|
||||
logging.print("mV (cell ");
|
||||
logging.print(battery_max_vno);
|
||||
logging.print("), Min: ");
|
||||
logging.print(battery_cell_min_v);
|
||||
logging.print("mV (cell ");
|
||||
logging.print(battery_min_vno);
|
||||
logging.print("), Imbalance: ");
|
||||
logging.print(battery_cell_deviation_mV);
|
||||
logging.println("mV.");
|
||||
|
||||
print_int_with_units("High Voltage Output Pins: ", battery_dcdcHvBusVolt, "V");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units("Low Voltage: ", battery_dcdcLvBusVolt, "V");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
print_int_with_units("DC/DC 12V current: ", battery_dcdcLvOutputCurrent, "A");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
|
||||
Serial.println("Values passed to the inverter: ");
|
||||
logging.println("Values passed to the inverter: ");
|
||||
print_SOC(" SOC: ", datalayer.battery.status.reported_soc);
|
||||
print_int_with_units(" Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units(" Max charge power: ", datalayer.battery.status.max_charge_power_W, "W");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
print_int_with_units(" Max temperature: ", ((int16_t)datalayer.battery.status.temperature_min_dC * 0.1), "°C");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units(" Min temperature: ", ((int16_t)datalayer.battery.status.temperature_max_dC * 0.1), "°C");
|
||||
Serial.println("");
|
||||
#endif //DEBUG_VIA_USB
|
||||
logging.println("");
|
||||
#endif //DEBUG_LOG
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
|
@ -1087,69 +1087,69 @@ void update_values_battery2() { //This function maps all the values fetched via
|
|||
}
|
||||
#endif // TESLA_MODEL_3Y_BATTERY
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
printFaultCodesIfActive_battery2();
|
||||
|
||||
Serial.print("STATUS: Contactor: ");
|
||||
Serial.print(contactorText[battery2_contactor]); //Display what state the contactor is in
|
||||
Serial.print(", HVIL: ");
|
||||
Serial.print(hvilStatusState[battery2_hvil_status]);
|
||||
Serial.print(", NegativeState: ");
|
||||
Serial.print(contactorState[battery2_packContNegativeState]);
|
||||
Serial.print(", PositiveState: ");
|
||||
Serial.print(contactorState[battery2_packContPositiveState]);
|
||||
Serial.print(", setState: ");
|
||||
Serial.print(contactorState[battery2_packContactorSetState]);
|
||||
Serial.print(", close allowed: ");
|
||||
Serial.print(battery2_packCtrsClosingAllowed);
|
||||
Serial.print(", Pyrotest: ");
|
||||
Serial.println(battery2_pyroTestInProgress);
|
||||
logging.print("STATUS: Contactor: ");
|
||||
logging.print(contactorText[battery2_contactor]); //Display what state the contactor is in
|
||||
logging.print(", HVIL: ");
|
||||
logging.print(hvilStatusState[battery2_hvil_status]);
|
||||
logging.print(", NegativeState: ");
|
||||
logging.print(contactorState[battery2_packContNegativeState]);
|
||||
logging.print(", PositiveState: ");
|
||||
logging.print(contactorState[battery2_packContPositiveState]);
|
||||
logging.print(", setState: ");
|
||||
logging.print(contactorState[battery2_packContactorSetState]);
|
||||
logging.print(", close allowed: ");
|
||||
logging.print(battery2_packCtrsClosingAllowed);
|
||||
logging.print(", Pyrotest: ");
|
||||
logging.println(battery2_pyroTestInProgress);
|
||||
|
||||
Serial.print("Battery2 values: ");
|
||||
Serial.print("Real SOC: ");
|
||||
Serial.print(battery2_soc_ui / 10.0, 1);
|
||||
logging.print("Battery2 values: ");
|
||||
logging.print("Real SOC: ");
|
||||
logging.print(battery2_soc_ui / 10.0, 1);
|
||||
print_int_with_units(", Battery2 voltage: ", battery2_volts, "V");
|
||||
print_int_with_units(", Battery2 HV current: ", (battery2_amps * 0.1), "A");
|
||||
Serial.print(", Fully charged?: ");
|
||||
logging.print(", Fully charged?: ");
|
||||
if (battery2_full_charge_complete)
|
||||
Serial.print("YES, ");
|
||||
logging.print("YES, ");
|
||||
else
|
||||
Serial.print("NO, ");
|
||||
logging.print("NO, ");
|
||||
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
|
||||
Serial.print("LFP chemistry detected!");
|
||||
logging.print("LFP chemistry detected!");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Cellstats, Max: ");
|
||||
Serial.print(battery2_cell_max_v);
|
||||
Serial.print("mV (cell ");
|
||||
Serial.print(battery2_max_vno);
|
||||
Serial.print("), Min: ");
|
||||
Serial.print(battery2_cell_min_v);
|
||||
Serial.print("mV (cell ");
|
||||
Serial.print(battery2_min_vno);
|
||||
Serial.print("), Imbalance: ");
|
||||
Serial.print(battery2_cell_deviation_mV);
|
||||
Serial.println("mV.");
|
||||
logging.println("");
|
||||
logging.print("Cellstats, Max: ");
|
||||
logging.print(battery2_cell_max_v);
|
||||
logging.print("mV (cell ");
|
||||
logging.print(battery2_max_vno);
|
||||
logging.print("), Min: ");
|
||||
logging.print(battery2_cell_min_v);
|
||||
logging.print("mV (cell ");
|
||||
logging.print(battery2_min_vno);
|
||||
logging.print("), Imbalance: ");
|
||||
logging.print(battery2_cell_deviation_mV);
|
||||
logging.println("mV.");
|
||||
|
||||
print_int_with_units("High Voltage Output Pins: ", battery2_dcdcHvBusVolt, "V");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units("Low Voltage: ", battery2_dcdcLvBusVolt, "V");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
print_int_with_units("DC/DC 12V current: ", battery2_dcdcLvOutputCurrent, "A");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
|
||||
Serial.println("Values passed to the inverter: ");
|
||||
logging.println("Values passed to the inverter: ");
|
||||
print_SOC(" SOC: ", datalayer.battery2.status.reported_soc);
|
||||
print_int_with_units(" Max discharge power: ", datalayer.battery2.status.max_discharge_power_W, "W");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units(" Max charge power: ", datalayer.battery2.status.max_charge_power_W, "W");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
print_int_with_units(" Max temperature: ", ((int16_t)datalayer.battery2.status.temperature_min_dC * 0.1), "°C");
|
||||
Serial.print(", ");
|
||||
logging.print(", ");
|
||||
print_int_with_units(" Min temperature: ", ((int16_t)datalayer.battery2.status.temperature_max_dC * 0.1), "°C");
|
||||
Serial.println("");
|
||||
#endif // DEBUG_VIA_USB
|
||||
logging.println("");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
@ -1261,32 +1261,32 @@ the first, for a few cycles, then stop all messages which causes the contactor
|
|||
}
|
||||
|
||||
void print_int_with_units(char* header, int value, char* units) {
|
||||
Serial.print(header);
|
||||
Serial.print(value);
|
||||
Serial.print(units);
|
||||
logging.print(header);
|
||||
logging.print(value);
|
||||
logging.print(units);
|
||||
}
|
||||
|
||||
void print_SOC(char* header, int SOC) {
|
||||
Serial.print(header);
|
||||
Serial.print(SOC / 100);
|
||||
Serial.print(".");
|
||||
logging.print(header);
|
||||
logging.print(SOC / 100);
|
||||
logging.print(".");
|
||||
int hundredth = SOC % 100;
|
||||
if (hundredth < 10)
|
||||
Serial.print(0);
|
||||
Serial.print(hundredth);
|
||||
Serial.println("%");
|
||||
logging.print(0);
|
||||
logging.print(hundredth);
|
||||
logging.println("%");
|
||||
}
|
||||
|
||||
void printFaultCodesIfActive() {
|
||||
if (battery_packCtrsClosingAllowed == 0) {
|
||||
Serial.println(
|
||||
logging.println(
|
||||
"ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: ");
|
||||
}
|
||||
if (battery_pyroTestInProgress == 1) {
|
||||
Serial.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
||||
logging.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
||||
}
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == false) {
|
||||
Serial.println(
|
||||
logging.println(
|
||||
"ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter "
|
||||
"OR "
|
||||
"disable the inverter protocol to proceed with contactor closing");
|
||||
|
@ -1355,14 +1355,14 @@ void printFaultCodesIfActive() {
|
|||
#ifdef DOUBLE_BATTERY
|
||||
void printFaultCodesIfActive_battery2() {
|
||||
if (battery2_packCtrsClosingAllowed == 0) {
|
||||
Serial.println(
|
||||
logging.println(
|
||||
"ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: ");
|
||||
}
|
||||
if (battery2_pyroTestInProgress == 1) {
|
||||
Serial.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
||||
logging.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
||||
}
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == false) {
|
||||
Serial.println(
|
||||
logging.println(
|
||||
"ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter "
|
||||
"OR "
|
||||
"disable the inverter protocol to proceed with contactor closing");
|
||||
|
@ -1433,7 +1433,7 @@ void printFaultCodesIfActive_battery2() {
|
|||
|
||||
void printDebugIfActive(uint8_t symbol, const char* message) {
|
||||
if (symbol == 1) {
|
||||
Serial.println(message);
|
||||
logging.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ CAN_frame TEST = {.FD = false,
|
|||
.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
|
||||
|
||||
void print_units(char* header, int value, char* units) {
|
||||
Serial.print(header);
|
||||
Serial.print(value);
|
||||
Serial.print(units);
|
||||
logging.print(header);
|
||||
logging.print(value);
|
||||
logging.print(units);
|
||||
}
|
||||
|
||||
void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */
|
||||
|
@ -54,8 +54,8 @@ void update_values_battery() { /* This function puts fake values onto the parame
|
|||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("FAKE Values going to inverter");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("FAKE 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 ");
|
||||
|
@ -65,7 +65,7 @@ void update_values_battery() { /* This function puts fake values onto the parame
|
|||
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 ");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,8 @@ void update_values_battery2() { // Handle the values coming in from battery #2
|
|||
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("FAKE Values battery 2 going to inverter");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("FAKE Values battery 2 going to inverter");
|
||||
print_units("SOH 2 %: ", (datalayer.battery2.status.soh_pptt * 0.01), "% ");
|
||||
print_units(", SOC 2 %: ", (datalayer.battery2.status.reported_soc * 0.01), "% ");
|
||||
print_units(", Voltage 2: ", (datalayer.battery2.status.voltage_dV * 0.1), "V ");
|
||||
|
@ -118,7 +118,7 @@ void update_values_battery2() { // Handle the values coming in from battery #2
|
|||
print_units(", Min temp 2: ", (datalayer.battery2.status.temperature_min_dC * 0.1), "°C ");
|
||||
print_units(", Max cell voltage 2: ", datalayer.battery2.status.cell_max_voltage_mV, "mV ");
|
||||
print_units(", Min cell voltage 2: ", datalayer.battery2.status.cell_min_voltage_mV, "mV ");
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -94,49 +94,49 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("BMS reported SOC%: ");
|
||||
Serial.println(SOC_BMS);
|
||||
Serial.print("Calculated SOC%: ");
|
||||
Serial.println(SOC_CALC);
|
||||
Serial.print("Rescaled SOC%: ");
|
||||
Serial.println(datalayer.battery.status.reported_soc / 100);
|
||||
Serial.print("Battery current: ");
|
||||
Serial.println(BATT_I);
|
||||
Serial.print("Battery voltage: ");
|
||||
Serial.println(BATT_U);
|
||||
Serial.print("Battery maximum voltage limit: ");
|
||||
Serial.println(MAX_U);
|
||||
Serial.print("Battery minimum voltage limit: ");
|
||||
Serial.println(MIN_U);
|
||||
Serial.print("Remaining Energy: ");
|
||||
Serial.println(remaining_capacity);
|
||||
Serial.print("Discharge limit: ");
|
||||
Serial.println(HvBattPwrLimDchaSoft);
|
||||
Serial.print("Battery Error Indication: ");
|
||||
Serial.println(BATT_ERR_INDICATION);
|
||||
Serial.print("Maximum battery temperature: ");
|
||||
Serial.println(BATT_T_MAX / 10);
|
||||
Serial.print("Minimum battery temperature: ");
|
||||
Serial.println(BATT_T_MIN / 10);
|
||||
Serial.print("Average battery temperature: ");
|
||||
Serial.println(BATT_T_AVG / 10);
|
||||
Serial.print("BMS Highest cell voltage: ");
|
||||
Serial.println(CELL_U_MAX * 10);
|
||||
Serial.print("BMS Lowest cell voltage: ");
|
||||
Serial.println(CELL_U_MIN * 10);
|
||||
Serial.print("BMS Highest cell nr: ");
|
||||
Serial.println(CELL_ID_U_MAX);
|
||||
Serial.print("Highest cell voltage: ");
|
||||
Serial.println(min_max_voltage[1]);
|
||||
Serial.print("Lowest cell voltage: ");
|
||||
Serial.println(min_max_voltage[0]);
|
||||
Serial.print("Cell voltage,");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("BMS reported SOC%: ");
|
||||
logging.println(SOC_BMS);
|
||||
logging.print("Calculated SOC%: ");
|
||||
logging.println(SOC_CALC);
|
||||
logging.print("Rescaled SOC%: ");
|
||||
logging.println(datalayer.battery.status.reported_soc / 100);
|
||||
logging.print("Battery current: ");
|
||||
logging.println(BATT_I);
|
||||
logging.print("Battery voltage: ");
|
||||
logging.println(BATT_U);
|
||||
logging.print("Battery maximum voltage limit: ");
|
||||
logging.println(MAX_U);
|
||||
logging.print("Battery minimum voltage limit: ");
|
||||
logging.println(MIN_U);
|
||||
logging.print("Remaining Energy: ");
|
||||
logging.println(remaining_capacity);
|
||||
logging.print("Discharge limit: ");
|
||||
logging.println(HvBattPwrLimDchaSoft);
|
||||
logging.print("Battery Error Indication: ");
|
||||
logging.println(BATT_ERR_INDICATION);
|
||||
logging.print("Maximum battery temperature: ");
|
||||
logging.println(BATT_T_MAX / 10);
|
||||
logging.print("Minimum battery temperature: ");
|
||||
logging.println(BATT_T_MIN / 10);
|
||||
logging.print("Average battery temperature: ");
|
||||
logging.println(BATT_T_AVG / 10);
|
||||
logging.print("BMS Highest cell voltage: ");
|
||||
logging.println(CELL_U_MAX * 10);
|
||||
logging.print("BMS Lowest cell voltage: ");
|
||||
logging.println(CELL_U_MIN * 10);
|
||||
logging.print("BMS Highest cell nr: ");
|
||||
logging.println(CELL_ID_U_MAX);
|
||||
logging.print("Highest cell voltage: ");
|
||||
logging.println(min_max_voltage[1]);
|
||||
logging.print("Lowest cell voltage: ");
|
||||
logging.println(min_max_voltage[0]);
|
||||
logging.print("Cell voltage,");
|
||||
while (cnt < 108) {
|
||||
Serial.print(cell_voltages[cnt++]);
|
||||
Serial.print(",");
|
||||
logging.print(cell_voltages[cnt++]);
|
||||
logging.print(",");
|
||||
}
|
||||
Serial.println(";");
|
||||
logging.println(";");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -148,8 +148,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638));
|
||||
else {
|
||||
BATT_I = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BATT_I not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_I not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -157,22 +157,22 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
MAX_U = (((rx_frame.data.u8[2] & 0x07) * 256.0 + rx_frame.data.u8[3]) * 0.25);
|
||||
else {
|
||||
//MAX_U = 0;
|
||||
//Serial.println("MAX_U not valid"); // Value toggles between true/false from BMS
|
||||
//logging.println("MAX_U not valid"); // Value toggles between true/false from BMS
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[4] & 0x08) == 0x08)
|
||||
MIN_U = (((rx_frame.data.u8[4] & 0x07) * 256.0 + rx_frame.data.u8[5]) * 0.25);
|
||||
else {
|
||||
//MIN_U = 0;
|
||||
//Serial.println("MIN_U not valid"); // Value toggles between true/false from BMS
|
||||
//logging.println("MIN_U not valid"); // Value toggles between true/false from BMS
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x08) == 0x08)
|
||||
BATT_U = (((rx_frame.data.u8[0] & 0x07) * 256.0 + rx_frame.data.u8[1]) * 0.25);
|
||||
else {
|
||||
BATT_U = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BATT_U not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_U not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
@ -189,8 +189,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
||||
else {
|
||||
BATT_ERR_INDICATION = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BATT_ERR_INDICATION not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_ERR_INDICATION not valid");
|
||||
#endif
|
||||
}
|
||||
if ((rx_frame.data.u8[0] & 0x20) == 0x20) {
|
||||
|
@ -201,8 +201,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
BATT_T_MAX = 0;
|
||||
BATT_T_MIN = 0;
|
||||
BATT_T_AVG = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BATT_T not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_T not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
@ -211,8 +211,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2);
|
||||
} else {
|
||||
HvBattPwrLimDchaSoft = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("HvBattPwrLimDchaSoft not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("HvBattPwrLimDchaSoft not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
@ -221,8 +221,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]);
|
||||
} else {
|
||||
SOC_BMS = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("SOC_BMS not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SOC_BMS not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -230,8 +230,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
CELL_U_MAX = ((rx_frame.data.u8[2] & 0x01) * 256 + rx_frame.data.u8[3]);
|
||||
else {
|
||||
CELL_U_MAX = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CELL_U_MAX not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_U_MAX not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -239,8 +239,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
CELL_U_MIN = ((rx_frame.data.u8[0] & 0x01) * 256.0 + rx_frame.data.u8[1]);
|
||||
else {
|
||||
CELL_U_MIN = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CELL_U_MIN not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_U_MIN not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -248,8 +248,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
CELL_ID_U_MAX = ((rx_frame.data.u8[4] & 0x01) * 256.0 + rx_frame.data.u8[5]);
|
||||
else {
|
||||
CELL_ID_U_MAX = 0;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("CELL_ID_U_MAX not valid");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_ID_U_MAX not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -101,8 +101,8 @@ void receive_can_charger(CAN_frame rx_frame) {
|
|||
case 0x308:
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.MsgID);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.MsgID);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
@ -177,15 +177,15 @@ void send_can_charger() {
|
|||
transmit_can(&charger_set_targets, can_config.charger);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
/* Serial echo every 5s of charger stats */
|
||||
if (currentMillis - previousMillis5000ms >= INTERVAL_5_S) {
|
||||
previousMillis5000ms = currentMillis;
|
||||
Serial.printf("Charger AC in IAC=%fA VAC=%fV\n", charger_stat_ACcur, charger_stat_ACvol);
|
||||
Serial.printf("Charger HV out IDC=%fA VDC=%fV\n", charger_stat_HVcur, charger_stat_HVvol);
|
||||
Serial.printf("Charger LV out IDC=%fA VDC=%fV\n", charger_stat_LVcur, charger_stat_LVvol);
|
||||
Serial.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled");
|
||||
Serial.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END);
|
||||
logging.printf("Charger AC in IAC=%fA VAC=%fV\n", charger_stat_ACcur, charger_stat_ACvol);
|
||||
logging.printf("Charger HV out IDC=%fA VDC=%fV\n", charger_stat_HVcur, charger_stat_HVvol);
|
||||
logging.printf("Charger LV out IDC=%fA VDC=%fV\n", charger_stat_LVcur, charger_stat_LVvol);
|
||||
logging.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled");
|
||||
logging.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
326
Software/src/communication/can/comm_can.cpp
Normal file
326
Software/src/communication/can/comm_can.cpp
Normal file
|
@ -0,0 +1,326 @@
|
|||
#include "comm_can.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
|
||||
CAN_device_t CAN_cfg; // CAN Config
|
||||
const int rx_queue_size = 10; // Receive Queue size
|
||||
volatile bool send_ok = 0;
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
|
||||
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
|
||||
static ACAN2515_Buffer16 gBuffer;
|
||||
#endif //CAN_ADDON
|
||||
#ifdef CANFD_ADDON
|
||||
ACAN2517FD canfd(MCP2517_CS, SPI, MCP2517_INT);
|
||||
#endif //CANFD_ADDON
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_CAN() {
|
||||
// CAN pins
|
||||
#ifdef CAN_SE_PIN
|
||||
pinMode(CAN_SE_PIN, OUTPUT);
|
||||
digitalWrite(CAN_SE_PIN, LOW);
|
||||
#endif // CAN_SE_PIN
|
||||
CAN_cfg.speed = CAN_SPEED_500KBPS;
|
||||
#ifdef NATIVECAN_250KBPS // Some component is requesting lower CAN speed
|
||||
CAN_cfg.speed = CAN_SPEED_250KBPS;
|
||||
#endif // NATIVECAN_250KBPS
|
||||
CAN_cfg.tx_pin_id = CAN_TX_PIN;
|
||||
CAN_cfg.rx_pin_id = CAN_RX_PIN;
|
||||
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
|
||||
// Init CAN Module
|
||||
ESP32Can.CANInit();
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||
#endif // DEBUG_LOG
|
||||
gBuffer.initWithSize(25);
|
||||
SPI.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
|
||||
ACAN2515Settings settings(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
|
||||
settings.mRequestedMode = ACAN2515Settings::NormalMode;
|
||||
const uint16_t errorCodeMCP = can.begin(settings, [] { can.isr(); });
|
||||
if (errorCodeMCP == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Can ok");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Error Can: 0x");
|
||||
logging.println(errorCodeMCP, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP_INIT_FAILURE, (uint8_t)errorCodeMCP);
|
||||
}
|
||||
#endif // CAN_ADDON
|
||||
|
||||
#ifdef CANFD_ADDON
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||
#endif // DEBUG_LOG
|
||||
SPI.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
|
||||
ACAN2517FDSettings settings(CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
|
||||
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
settings.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
|
||||
#else // not USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
settings.mRequestedMode = ACAN2517FDSettings::NormalFD; // ListenOnly / Normal20B / NormalFD
|
||||
#endif // USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
const uint32_t errorCode = canfd.begin(settings, [] { canfd.isr(); });
|
||||
canfd.poll();
|
||||
if (errorCode == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Bit Rate prescaler: ");
|
||||
logging.println(settings.mBitRatePrescaler);
|
||||
logging.print("Arbitration Phase segment 1: ");
|
||||
logging.print(settings.mArbitrationPhaseSegment1);
|
||||
logging.print(" segment 2: ");
|
||||
logging.print(settings.mArbitrationPhaseSegment2);
|
||||
logging.print(" SJW: ");
|
||||
logging.println(settings.mArbitrationSJW);
|
||||
logging.print("Actual Arbitration Bit Rate: ");
|
||||
logging.print(settings.actualArbitrationBitRate());
|
||||
logging.print(" bit/s");
|
||||
logging.print(" (Exact:");
|
||||
logging.println(settings.exactArbitrationBitRate() ? "yes)" : "no)");
|
||||
logging.print("Arbitration Sample point: ");
|
||||
logging.print(settings.arbitrationSamplePointFromBitStart());
|
||||
logging.println("%");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("CAN-FD Configuration error 0x");
|
||||
logging.println(errorCode, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANFD_INIT_FAILURE, (uint8_t)errorCode);
|
||||
}
|
||||
#endif // CANFD_ADDON
|
||||
}
|
||||
|
||||
// Transmit functions
|
||||
|
||||
void transmit_can(CAN_frame* tx_frame, int interface) {
|
||||
if (!allowed_to_send_CAN) {
|
||||
return;
|
||||
}
|
||||
print_can_frame(*tx_frame, frameDirection(MSG_TX));
|
||||
|
||||
switch (interface) {
|
||||
case CAN_NATIVE:
|
||||
CAN_frame_t frame;
|
||||
frame.MsgID = tx_frame->ID;
|
||||
frame.FIR.B.FF = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
frame.FIR.B.DLC = tx_frame->DLC;
|
||||
frame.FIR.B.RTR = CAN_no_RTR;
|
||||
for (uint8_t i = 0; i < tx_frame->DLC; i++) {
|
||||
frame.data.u8[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
ESP32Can.CANWriteFrame(&frame);
|
||||
break;
|
||||
case CAN_ADDON_MCP2515: {
|
||||
#ifdef CAN_ADDON
|
||||
//Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2
|
||||
CANMessage MCP2515Frame;
|
||||
MCP2515Frame.id = tx_frame->ID;
|
||||
MCP2515Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2515Frame.len = tx_frame->DLC;
|
||||
MCP2515Frame.rtr = false;
|
||||
for (uint8_t i = 0; i < MCP2515Frame.len; i++) {
|
||||
MCP2515Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
can.tryToSend(MCP2515Frame);
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //CAN_ADDON
|
||||
} break;
|
||||
case CANFD_NATIVE:
|
||||
case CANFD_ADDON_MCP2518: {
|
||||
#ifdef CANFD_ADDON
|
||||
CANFDMessage MCP2518Frame;
|
||||
if (tx_frame->FD) {
|
||||
MCP2518Frame.type = CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH;
|
||||
} else { //Classic CAN message
|
||||
MCP2518Frame.type = CANFDMessage::CAN_DATA;
|
||||
}
|
||||
MCP2518Frame.id = tx_frame->ID;
|
||||
MCP2518Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2518Frame.len = tx_frame->DLC;
|
||||
for (uint8_t i = 0; i < MCP2518Frame.len; i++) {
|
||||
MCP2518Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
send_ok = canfd.tryToSend(MCP2518Frame);
|
||||
if (!send_ok) {
|
||||
set_event(EVENT_CANFD_BUFFER_FULL, interface);
|
||||
} else {
|
||||
clear_event(EVENT_CANFD_BUFFER_FULL);
|
||||
}
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //CANFD_ADDON
|
||||
} break;
|
||||
default:
|
||||
// Invalid interface sent with function call. TODO: Raise event that coders messed up
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void send_can() {
|
||||
if (!allowed_to_send_CAN) {
|
||||
return;
|
||||
}
|
||||
send_can_battery();
|
||||
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
send_can_inverter();
|
||||
#endif // CAN_INVERTER_SELECTED
|
||||
|
||||
#ifdef CHARGER_SELECTED
|
||||
send_can_charger();
|
||||
#endif // CHARGER_SELECTED
|
||||
}
|
||||
|
||||
// Receive functions
|
||||
|
||||
void receive_can(CAN_frame* rx_frame, int interface) {
|
||||
print_can_frame(*rx_frame, frameDirection(MSG_RX));
|
||||
|
||||
if (interface == can_config.battery) {
|
||||
receive_can_battery(*rx_frame);
|
||||
#ifdef CHADEMO_BATTERY
|
||||
ISA_handleFrame(rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.inverter) {
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
receive_can_inverter(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.battery_double) {
|
||||
#ifdef DOUBLE_BATTERY
|
||||
receive_can_battery2(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
if (interface == can_config.charger) {
|
||||
#ifdef CHARGER_SELECTED
|
||||
receive_can_charger(*rx_frame);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void receive_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port
|
||||
CAN_frame_t rx_frame_native;
|
||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame_native, 0) == pdTRUE) {
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = rx_frame_native.MsgID;
|
||||
if (rx_frame_native.FIR.B.FF == CAN_frame_std) {
|
||||
rx_frame.ext_ID = false;
|
||||
} else { //CAN_frame_ext == 1
|
||||
rx_frame.ext_ID = true;
|
||||
}
|
||||
rx_frame.DLC = rx_frame_native.FIR.B.DLC;
|
||||
for (uint8_t i = 0; i < rx_frame.DLC && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = rx_frame_native.data.u8[i];
|
||||
}
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CAN_NATIVE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
void receive_can_addon() { // This section checks if we have a complete CAN message incoming on add-on CAN port
|
||||
CAN_frame rx_frame; // Struct with our CAN format
|
||||
CANMessage MCP2515Frame; // Struct with ACAN2515 library format, needed to use the MCP2515 library
|
||||
|
||||
if (can.available()) {
|
||||
can.receive(MCP2515Frame);
|
||||
|
||||
rx_frame.ID = MCP2515Frame.id;
|
||||
rx_frame.ext_ID = MCP2515Frame.ext ? CAN_frame_ext : CAN_frame_std;
|
||||
rx_frame.DLC = MCP2515Frame.len;
|
||||
for (uint8_t i = 0; i < MCP2515Frame.len && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = MCP2515Frame.data[i];
|
||||
}
|
||||
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CAN_ADDON_MCP2515);
|
||||
}
|
||||
}
|
||||
#endif // CAN_ADDON
|
||||
|
||||
#ifdef CANFD_ADDON
|
||||
// Functions
|
||||
void receive_canfd_addon() { // This section checks if we have a complete CAN-FD message incoming
|
||||
CANFDMessage frame;
|
||||
int count = 0;
|
||||
while (canfd.available() && count++ < 16) {
|
||||
canfd.receive(frame);
|
||||
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = frame.id;
|
||||
rx_frame.ext_ID = frame.ext;
|
||||
rx_frame.DLC = frame.len;
|
||||
memcpy(rx_frame.data.u8, frame.data, MIN(rx_frame.DLC, 64));
|
||||
//message incoming, pass it on to the handler
|
||||
receive_can(&rx_frame, CANFD_ADDON_MCP2518);
|
||||
receive_can(&rx_frame, CANFD_NATIVE);
|
||||
}
|
||||
}
|
||||
#endif // CANFD_ADDON
|
||||
|
||||
// Support functions
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir) {
|
||||
#ifdef DEBUG_CAN_DATA // If enabled in user settings, print out the CAN messages via USB
|
||||
uint8_t i = 0;
|
||||
Serial.print("(");
|
||||
Serial.print(millis() / 1000.0);
|
||||
(msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 ");
|
||||
Serial.print(frame.ID, HEX);
|
||||
Serial.print(" [");
|
||||
Serial.print(frame.DLC);
|
||||
Serial.print("] ");
|
||||
for (i = 0; i < frame.DLC; i++) {
|
||||
Serial.print(frame.data.u8[i] < 16 ? "0" : "");
|
||||
Serial.print(frame.data.u8[i], HEX);
|
||||
if (i < frame.DLC - 1)
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
#endif // DEBUG_CAN_DATA
|
||||
|
||||
if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording
|
||||
char* message_string = datalayer.system.info.logged_can_messages;
|
||||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
|
||||
if (offset + 128 > sizeof(datalayer.system.info.logged_can_messages)) {
|
||||
// Not enough space, reset and start from the beginning
|
||||
offset = 0;
|
||||
}
|
||||
unsigned long currentTime = millis();
|
||||
// Add timestamp
|
||||
offset += snprintf(message_string + offset, message_string_size - offset, "(%lu.%03lu) ", currentTime / 1000,
|
||||
currentTime % 1000);
|
||||
|
||||
// Add direction. The 0 and 1 after RX and TX ensures that SavvyCAN puts TX and RX in a different bus.
|
||||
offset +=
|
||||
snprintf(message_string + offset, message_string_size - offset, "%s ", (msgDir == MSG_RX) ? "RX0" : "TX1");
|
||||
|
||||
// Add ID and DLC
|
||||
offset += snprintf(message_string + offset, message_string_size - offset, "%X [%u] ", frame.ID, frame.DLC);
|
||||
|
||||
// Add data bytes
|
||||
for (uint8_t i = 0; i < frame.DLC; i++) {
|
||||
if (i < frame.DLC - 1) {
|
||||
offset += snprintf(message_string + offset, message_string_size - offset, "%02X ", frame.data.u8[i]);
|
||||
} else {
|
||||
offset += snprintf(message_string + offset, message_string_size - offset, "%02X", frame.data.u8[i]);
|
||||
}
|
||||
}
|
||||
// Add linebreak
|
||||
offset += snprintf(message_string + offset, message_string_size - offset, "\n");
|
||||
|
||||
datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer
|
||||
}
|
||||
}
|
93
Software/src/communication/can/comm_can.h
Normal file
93
Software/src/communication/can/comm_can.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
#ifndef _COMM_CAN_H_
|
||||
#define _COMM_CAN_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../devboard/utils/events.h"
|
||||
#include "../../devboard/utils/value_mapping.h"
|
||||
#include "../../lib/me-no-dev-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
#ifdef CAN_ADDON
|
||||
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||
#endif //CAN_ADDON
|
||||
#ifdef CANFD_ADDON
|
||||
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
|
||||
#endif //CANFD_ADDON
|
||||
|
||||
enum frameDirection { MSG_RX, MSG_TX }; //RX = 0, TX = 1
|
||||
|
||||
/**
|
||||
* @brief Initialization function for CAN.
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_CAN();
|
||||
|
||||
/**
|
||||
* @brief Transmit one CAN frame
|
||||
*
|
||||
* @param[in] CAN_frame* tx_frame
|
||||
* @param[in] int interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void transmit_can();
|
||||
|
||||
/**
|
||||
* @brief Send CAN messages to all components
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void send_can();
|
||||
|
||||
/**
|
||||
* @brief Receive CAN messages from all interfaces
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void receive_can();
|
||||
|
||||
/**
|
||||
* @brief Receive CAN messages from CAN tranceiver natively installed on Lilygo hardware
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void receive_can_native();
|
||||
|
||||
/**
|
||||
* @brief Receive CAN messages from CAN addon chip
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void receive_can_addon();
|
||||
|
||||
/**
|
||||
* @brief Receive CAN messages from CANFD addon chip
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void receive_canfd_addon();
|
||||
|
||||
/**
|
||||
* @brief print CAN frames via USB
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,204 @@
|
|||
#include "comm_contactorcontrol.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
#ifndef CONTACTOR_CONTROL
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
#error CONTACTOR_CONTROL needs to be enabled for PWM_CONTACTOR_CONTROL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||
State contactorStatus = DISCONNECTED;
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic
|
||||
#undef ON
|
||||
#define ON 0
|
||||
#undef OFF
|
||||
#define OFF 1
|
||||
#endif //NC_CONTACTORS
|
||||
|
||||
#define MAX_ALLOWED_FAULT_TICKS 1000
|
||||
#define NEGATIVE_CONTACTOR_TIME_MS \
|
||||
500 // Time after negative contactor is turned on, to start precharge (not actual precharge time!)
|
||||
#define PRECHARGE_COMPLETED_TIME_MS \
|
||||
1000 // After successful precharge, resistor is turned off after this delay (and contactors are economized if PWM enabled)
|
||||
#define PWM_Freq 20000 // 20 kHz frequency, beyond audible range
|
||||
#define PWM_Res 10 // 10 Bit resolution 0 to 1023, maps 'nicely' to 0% 100%
|
||||
#define PWM_HOLD_DUTY 250
|
||||
#define PWM_OFF_DUTY 0
|
||||
#define PWM_ON_DUTY 1023
|
||||
#define PWM_Positive_Channel 0
|
||||
#define PWM_Negative_Channel 1
|
||||
unsigned long prechargeStartTime = 0;
|
||||
unsigned long negativeStartTime = 0;
|
||||
unsigned long prechargeCompletedTime = 0;
|
||||
unsigned long timeSpentInFaultedMode = 0;
|
||||
#endif
|
||||
|
||||
void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (pwm_freq != 0xFFFF) {
|
||||
ledcWrite(pin, pwm_freq);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (direction == 1) {
|
||||
digitalWrite(pin, HIGH);
|
||||
} else { // 0
|
||||
digitalWrite(pin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_contactors() {
|
||||
// Init contactor pins
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
// Setup PWM Channel Frequency and Resolution
|
||||
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
|
||||
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
|
||||
// Set all pins OFF (0% PWM)
|
||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
|
||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
|
||||
#else //Normal CONTACTOR_CONTROL
|
||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
#endif // Precharge never has PWM regardless of setting
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
#endif // CONTACTOR_CONTROL
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
// Init BMS contactor
|
||||
#ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor
|
||||
pinMode(BMS_POWER, OUTPUT);
|
||||
digitalWrite(BMS_POWER, HIGH);
|
||||
#endif // HW_STARK
|
||||
}
|
||||
|
||||
// Main functions
|
||||
void handle_contactors() {
|
||||
#ifdef BYD_SMA
|
||||
datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
|
||||
#endif // BYD_SMA
|
||||
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
handle_contactors_battery2();
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
// First check if we have any active errors, incase we do, turn off the battery
|
||||
if (datalayer.battery.status.bms_status == FAULT) {
|
||||
timeSpentInFaultedMode++;
|
||||
} else {
|
||||
timeSpentInFaultedMode = 0;
|
||||
}
|
||||
|
||||
//handle contactor control SHUTDOWN_REQUESTED
|
||||
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
|
||||
contactorStatus = SHUTDOWN_REQUESTED;
|
||||
}
|
||||
|
||||
if (contactorStatus == SHUTDOWN_REQUESTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
|
||||
datalayer.system.status.contactors_engaged = false;
|
||||
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
|
||||
}
|
||||
|
||||
// After that, check if we are OK to start turning on the battery
|
||||
if (contactorStatus == DISCONNECTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
|
||||
if (datalayer.system.status.battery_allows_contactor_closing &&
|
||||
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) {
|
||||
contactorStatus = START_PRECHARGE;
|
||||
}
|
||||
}
|
||||
|
||||
// In case the inverter requests contactors to open, set the state accordingly
|
||||
if (contactorStatus == COMPLETED) {
|
||||
//Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable)
|
||||
if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.settings.equipment_stop_active) {
|
||||
contactorStatus = DISCONNECTED;
|
||||
}
|
||||
// Skip running the state machine below if it has already completed
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
if (currentTime < INTERVAL_10_S) {
|
||||
// Skip running the state machine before system has started up.
|
||||
// Gives the system some time to detect any faults from battery before blindly just engaging the contactors
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
|
||||
switch (contactorStatus) {
|
||||
case START_PRECHARGE:
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
prechargeStartTime = currentTime;
|
||||
contactorStatus = PRECHARGE;
|
||||
break;
|
||||
|
||||
case PRECHARGE:
|
||||
if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
||||
set(PRECHARGE_PIN, ON);
|
||||
negativeStartTime = currentTime;
|
||||
contactorStatus = POSITIVE;
|
||||
}
|
||||
break;
|
||||
|
||||
case POSITIVE:
|
||||
if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) {
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
prechargeCompletedTime = currentTime;
|
||||
contactorStatus = PRECHARGE_OFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRECHARGE_OFF:
|
||||
if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
contactorStatus = COMPLETED;
|
||||
datalayer.system.status.contactors_engaged = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // CONTACTOR_CONTROL
|
||||
}
|
||||
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
void handle_contactors_battery2() {
|
||||
if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing) {
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, ON);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, ON);
|
||||
datalayer.system.status.contactors_battery2_engaged = true;
|
||||
} else { // Closing contactors on secondary battery not allowed
|
||||
set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
set(SECOND_POSITIVE_CONTACTOR_PIN, OFF);
|
||||
datalayer.system.status.contactors_battery2_engaged = false;
|
||||
}
|
||||
}
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef _COMM_CONTACTORCONTROL_H_
|
||||
#define _COMM_CONTACTORCONTROL_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../devboard/utils/events.h"
|
||||
|
||||
/**
|
||||
* @brief Contactor initialization
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_contactors();
|
||||
|
||||
/**
|
||||
* @brief Handle contactors
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void handle_contactors();
|
||||
|
||||
/**
|
||||
* @brief Handle contactors of battery 2
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void handle_contactors_battery2();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#include "comm_equipmentstopbutton.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
const unsigned long equipment_button_long_press_duration =
|
||||
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
|
||||
const unsigned long equipment_button_debounce_duration = 200; // 200ms for debouncing the button
|
||||
unsigned long timeSincePress = 0; // Variable to store the time since the last press
|
||||
DebouncedButton equipment_stop_button; // Debounced button object
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
||||
|
||||
// Initialization functions
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
void init_equipment_stop_button() {
|
||||
//using external pullup resistors NC
|
||||
pinMode(EQUIPMENT_STOP_PIN, INPUT);
|
||||
// Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time
|
||||
initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration);
|
||||
}
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
||||
|
||||
// Main functions
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
void monitor_equipment_stop_button() {
|
||||
|
||||
ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress);
|
||||
|
||||
if (equipment_stop_behavior == LATCHING_SWITCH) {
|
||||
if (changed_state == PRESSED) {
|
||||
// Changed to ON – initiating equipment stop.
|
||||
setBatteryPause(true, false, true);
|
||||
} else if (changed_state == RELEASED) {
|
||||
// Changed to OFF – ending equipment stop.
|
||||
setBatteryPause(false, false, false);
|
||||
}
|
||||
} else if (equipment_stop_behavior == MOMENTARY_SWITCH) {
|
||||
if (changed_state == RELEASED) { // button is released
|
||||
|
||||
if (timeSincePress < equipment_button_long_press_duration) {
|
||||
// Short press detected, trigger equipment stop
|
||||
setBatteryPause(true, false, true);
|
||||
} else {
|
||||
// Long press detected, reset equipment stop state
|
||||
setBatteryPause(false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef _COMM_EQUIPMENTSTOPBUTTON_H_
|
||||
#define _COMM_EQUIPMENTSTOPBUTTON_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#include "../../devboard/utils/debounce_button.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialization of equipment stop button
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_equipment_stop_button();
|
||||
|
||||
/**
|
||||
* @brief Monitor equipment stop button
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void monitor_equipment_stop_button();
|
||||
|
||||
#endif
|
125
Software/src/communication/nvm/comm_nvm.cpp
Normal file
125
Software/src/communication/nvm/comm_nvm.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include "comm_nvm.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
Preferences settings; // Store user settings
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_stored_settings() {
|
||||
static uint32_t temp = 0;
|
||||
// ATTENTION ! The maximum length for settings keys is 15 characters
|
||||
settings.begin("batterySettings", false);
|
||||
|
||||
// Always get the equipment stop status
|
||||
datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false);
|
||||
if (datalayer.system.settings.equipment_stop_active) {
|
||||
set_event(EVENT_EQUIPMENT_STOP, 1);
|
||||
}
|
||||
|
||||
#ifndef LOAD_SAVED_SETTINGS_ON_BOOT
|
||||
settings.clear(); // If this clear function is executed, no settings will be read from storage
|
||||
|
||||
//always save the equipment stop status
|
||||
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
|
||||
#endif // LOAD_SAVED_SETTINGS_ON_BOOT
|
||||
|
||||
#ifdef WIFI
|
||||
char tempSSIDstring[63]; // Allocate buffer with sufficient size
|
||||
size_t lengthSSID = settings.getString("SSID", tempSSIDstring, sizeof(tempSSIDstring));
|
||||
if (lengthSSID > 0) { // Successfully read the string from memory. Set it to SSID!
|
||||
ssid = tempSSIDstring;
|
||||
} else { // Reading from settings failed. Do nothing with SSID. Raise event?
|
||||
}
|
||||
char tempPasswordString[63]; // Allocate buffer with sufficient size
|
||||
size_t lengthPassword = settings.getString("PASSWORD", tempPasswordString, sizeof(tempPasswordString));
|
||||
if (lengthPassword > 7) { // Successfully read the string from memory. Set it to password!
|
||||
password = tempPasswordString;
|
||||
} else { // Reading from settings failed. Do nothing with SSID. Raise event?
|
||||
}
|
||||
#endif // WIFI
|
||||
|
||||
temp = settings.getUInt("BATTERY_WH_MAX", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.info.total_capacity_Wh = temp;
|
||||
}
|
||||
temp = settings.getUInt("MAXPERCENTAGE", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_percentage = temp * 10; // Multiply by 10 for backwards compatibility
|
||||
}
|
||||
temp = settings.getUInt("MINPERCENTAGE", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.min_percentage = temp * 10; // Multiply by 10 for backwards compatibility
|
||||
}
|
||||
temp = settings.getUInt("MAXCHARGEAMP", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_charge_dA = temp;
|
||||
}
|
||||
temp = settings.getUInt("MAXDISCHARGEAMP", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_discharge_dA = temp;
|
||||
}
|
||||
datalayer.battery.settings.soc_scaling_active = settings.getBool("USE_SCALED_SOC", false);
|
||||
temp = settings.getUInt("TARGETCHVOLT", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_charge_voltage_dV = temp;
|
||||
}
|
||||
temp = settings.getUInt("TARGETDISCHVOLT", false);
|
||||
if (temp != 0) {
|
||||
datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp;
|
||||
}
|
||||
datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false);
|
||||
settings.end();
|
||||
}
|
||||
|
||||
void store_settings_equipment_stop() {
|
||||
settings.begin("batterySettings", false);
|
||||
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
|
||||
settings.end();
|
||||
}
|
||||
|
||||
void store_settings() {
|
||||
// ATTENTION ! The maximum length for settings keys is 15 characters
|
||||
if (!settings.begin("batterySettings", false)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WIFI
|
||||
if (!settings.putString("SSID", String(ssid.c_str()))) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 1);
|
||||
}
|
||||
if (!settings.putString("PASSWORD", String(password.c_str()))) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 3);
|
||||
}
|
||||
if (!settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 4);
|
||||
}
|
||||
if (!settings.putUInt("MAXPERCENTAGE", datalayer.battery.settings.max_percentage / 10)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 5);
|
||||
}
|
||||
if (!settings.putUInt("MINPERCENTAGE", datalayer.battery.settings.min_percentage / 10)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 6);
|
||||
}
|
||||
if (!settings.putUInt("MAXCHARGEAMP", datalayer.battery.settings.max_user_set_charge_dA)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 7);
|
||||
}
|
||||
if (!settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.settings.max_user_set_discharge_dA)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 8);
|
||||
}
|
||||
if (!settings.putBool("USEVOLTLIMITS", datalayer.battery.settings.user_set_voltage_limits_active)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 9);
|
||||
}
|
||||
if (!settings.putUInt("TARGETCHVOLT", datalayer.battery.settings.max_user_set_charge_voltage_dV)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 10);
|
||||
}
|
||||
if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
|
||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 11);
|
||||
}
|
||||
settings.end(); // Close preferences handle
|
||||
}
|
37
Software/src/communication/nvm/comm_nvm.h
Normal file
37
Software/src/communication/nvm/comm_nvm.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef _COMM_NVM_H_
|
||||
#define _COMM_NVM_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../devboard/utils/events.h"
|
||||
#include "../../devboard/wifi/wifi.h"
|
||||
|
||||
/**
|
||||
* @brief Initialization of setting storage
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_stored_settings();
|
||||
|
||||
/**
|
||||
* @brief Store settings of equipment stop button
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void store_settings_equipment_stop();
|
||||
|
||||
/**
|
||||
* @brief Store settings
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void store_settings();
|
||||
|
||||
#endif
|
51
Software/src/communication/rs485/comm_rs485.cpp
Normal file
51
Software/src/communication/rs485/comm_rs485.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "comm_rs485.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
#define MB_RTU_NUM_VALUES 13100
|
||||
uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
||||
ModbusServerRTU MBserver(Serial2, 2000);
|
||||
#endif
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
#define SERIAL_LINK_BAUDRATE 112500
|
||||
#endif
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_rs485() {
|
||||
// Set up Modbus RTU Server
|
||||
#ifdef RS485_EN_PIN
|
||||
pinMode(RS485_EN_PIN, OUTPUT);
|
||||
digitalWrite(RS485_EN_PIN, HIGH);
|
||||
#endif // RS485_EN_PIN
|
||||
#ifdef RS485_SE_PIN
|
||||
pinMode(RS485_SE_PIN, OUTPUT);
|
||||
digitalWrite(RS485_SE_PIN, HIGH);
|
||||
#endif // RS485_SE_PIN
|
||||
#ifdef PIN_5V_EN
|
||||
pinMode(PIN_5V_EN, OUTPUT);
|
||||
digitalWrite(PIN_5V_EN, HIGH);
|
||||
#endif // PIN_5V_EN
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
Serial2.begin(57600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif // RS485_INVERTER_SELECTED
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
#ifdef BYD_MODBUS
|
||||
// Init Static data to the RTU Modbus
|
||||
handle_static_data_modbus_byd();
|
||||
#endif // BYD_MODBUS
|
||||
// Init Serial2 connected to the RTU Modbus
|
||||
RTUutils::prepareHardwareSerial(Serial2);
|
||||
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
// Register served function code worker for server
|
||||
MBserver.registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
|
||||
MBserver.registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
|
||||
MBserver.registerWorker(MBTCP_ID, WRITE_MULT_REGISTERS, &FC16);
|
||||
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
||||
// Start ModbusRTU background task
|
||||
MBserver.begin(Serial2, MODBUS_CORE);
|
||||
#endif // MODBUS_INVERTER_SELECTED
|
||||
}
|
19
Software/src/communication/rs485/comm_rs485.h
Normal file
19
Software/src/communication/rs485/comm_rs485.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef _COMM_RS485_H_
|
||||
#define _COMM_RS485_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#include "../../lib/eModbus-eModbus/Logging.h"
|
||||
#include "../../lib/eModbus-eModbus/ModbusServerRTU.h"
|
||||
#include "../../lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
||||
|
||||
/**
|
||||
* @brief Initialization of RS485
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_rs485();
|
||||
|
||||
#endif
|
35
Software/src/communication/seriallink/comm_seriallink.cpp
Normal file
35
Software/src/communication/seriallink/comm_seriallink.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "comm_seriallink.h"
|
||||
#include "../../include.h"
|
||||
|
||||
// Parameters
|
||||
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
#define SERIAL_LINK_BAUDRATE 112500
|
||||
#endif
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_serialDataLink() {
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
Serial2.begin(SERIAL_LINK_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
||||
}
|
||||
|
||||
// Main functions
|
||||
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
void run_serialDataLink() {
|
||||
static unsigned long updateTime = 0;
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
||||
updateTime = currentMillis;
|
||||
#ifdef SERIAL_LINK_RECEIVER
|
||||
manageSerialLinkReceiver();
|
||||
#endif
|
||||
#ifdef SERIAL_LINK_TRANSMITTER
|
||||
manageSerialLinkTransmitter();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
17
Software/src/communication/seriallink/comm_seriallink.h
Normal file
17
Software/src/communication/seriallink/comm_seriallink.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef _COMM_SERIALLINK_H_
|
||||
#define _COMM_SERIALLINK_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
/**
|
||||
* @brief Initialization of serial data link
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_serialDataLink();
|
||||
|
||||
void run_serialDataLink();
|
||||
|
||||
#endif
|
|
@ -107,10 +107,20 @@ typedef struct {
|
|||
* you want the inverter to be able to use. At this real SOC, the inverter
|
||||
* will "see" 100% */
|
||||
uint16_t max_percentage = BATTERY_MAXPERCENTAGE;
|
||||
|
||||
/** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */
|
||||
uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP;
|
||||
/** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */
|
||||
uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP;
|
||||
|
||||
/** User specified discharge/charge voltages in use. Set to true to use user specified values */
|
||||
/** Some inverters like to see a specific target voltage for charge/discharge. Use these values to override automatic voltage limits*/
|
||||
bool user_set_voltage_limits_active = BATTERY_USE_VOLTAGE_LIMITS;
|
||||
/** The user specified maximum allowed charge voltage, in deciVolt. 4000 = 400.0 V */
|
||||
uint16_t max_user_set_charge_voltage_dV = BATTERY_MAX_CHARGE_VOLTAGE;
|
||||
/** The user specified maximum allowed discharge voltage, in deciVolt. 3000 = 300.0 V */
|
||||
uint16_t max_user_set_discharge_voltage_dV = BATTERY_MAX_DISCHARGE_VOLTAGE;
|
||||
|
||||
} DATALAYER_BATTERY_SETTINGS_TYPE;
|
||||
|
||||
typedef struct {
|
||||
|
@ -133,6 +143,7 @@ typedef struct {
|
|||
char inverter_protocol[64] = {0};
|
||||
/** array with incoming CAN messages, for displaying on webserver */
|
||||
char logged_can_messages[15000] = {0};
|
||||
size_t logged_can_messages_offset = 0;
|
||||
/** bool, determines if CAN messages should be logged for webserver */
|
||||
bool can_logging_active = false;
|
||||
|
||||
|
|
|
@ -251,6 +251,11 @@ typedef struct {
|
|||
} DATALAYER_INFO_TESLA;
|
||||
|
||||
typedef struct {
|
||||
/** uint8_t */
|
||||
/** Battery info, stores raw HEX values for ASCII chars */
|
||||
uint8_t BatterySerialNumber[15] = {0};
|
||||
uint8_t BatteryPartNumber[7] = {0};
|
||||
uint8_t BMSIDcode[8] = {0};
|
||||
/** uint8_t */
|
||||
/** Enum, ZE0 = 0, AZE0 = 1, ZE1 = 2 */
|
||||
uint8_t LEAF_gen = 0;
|
||||
|
@ -314,6 +319,78 @@ typedef struct {
|
|||
|
||||
} DATALAYER_INFO_NISSAN_LEAF;
|
||||
|
||||
typedef struct {
|
||||
/** uint8_t */
|
||||
/** Service disconnect switch status */
|
||||
bool SDSW = 0;
|
||||
/** uint8_t */
|
||||
/** Pilotline status */
|
||||
bool pilotline = 0;
|
||||
/** uint8_t */
|
||||
/** Transportation mode status */
|
||||
bool transportmode = 0;
|
||||
/** uint8_t */
|
||||
/** Componentprotection mode status */
|
||||
bool componentprotection = 0;
|
||||
/** uint8_t */
|
||||
/** Shutdown status */
|
||||
bool shutdown_active = 0;
|
||||
/** uint8_t */
|
||||
/** Battery heating status */
|
||||
bool battery_heating = 0;
|
||||
/** uint8_t */
|
||||
/** All realtime_ warnings have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3 */
|
||||
uint8_t rt_overcurrent = 0;
|
||||
uint8_t rt_CAN_fault = 0;
|
||||
uint8_t rt_overcharge = 0;
|
||||
uint8_t rt_SOC_high = 0;
|
||||
uint8_t rt_SOC_low = 0;
|
||||
uint8_t rt_SOC_jumping = 0;
|
||||
uint8_t rt_temp_difference = 0;
|
||||
uint8_t rt_cell_overtemp = 0;
|
||||
uint8_t rt_cell_undertemp = 0;
|
||||
uint8_t rt_battery_overvolt = 0;
|
||||
uint8_t rt_battery_undervol = 0;
|
||||
uint8_t rt_cell_overvolt = 0;
|
||||
uint8_t rt_cell_undervol = 0;
|
||||
uint8_t rt_cell_imbalance = 0;
|
||||
uint8_t rt_battery_unathorized = 0;
|
||||
/** uint8_t */
|
||||
/** HVIL status, 0 = Init, 1 = Closed, 2 = Open!, 3 = Fault */
|
||||
uint8_t HVIL = 0;
|
||||
/** uint8_t */
|
||||
/** 0 = HV inactive, 1 = HV active, 2 = Balancing, 3 = Extern charging, 4 = AC charging, 5 = Battery error, 6 = DC charging, 7 = init */
|
||||
uint8_t BMS_mode = 0;
|
||||
/** uint8_t */
|
||||
/** 1 = Battery display, 4 = Battery display OK, 4 = Display battery charging, 6 = Display battery check, 7 = Fault */
|
||||
uint8_t battery_diagnostic = 0;
|
||||
/** uint8_t */
|
||||
/** 0 = init, 1 = no open HV line detected, 2 = open HV line , 3 = fault */
|
||||
uint8_t status_HV_line = 0;
|
||||
/** uint8_t */
|
||||
/** 0 = OK, 1 = Not OK, 0x06 = init, 0x07 = fault */
|
||||
uint8_t warning_support = 0;
|
||||
/** uint32_t */
|
||||
/** Isolation resistance in kOhm */
|
||||
uint32_t isolation_resistance = 0;
|
||||
/** uint8_t */
|
||||
/** 0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error */
|
||||
uint8_t BMS_status_voltage_free = 0;
|
||||
/** uint8_t */
|
||||
/** 0 Component_IO, 1 Restricted_CompFkt_Isoerror_I, 2 Restricted_CompFkt_Isoerror_II, 3 Restricted_CompFkt_Interlock, 4 Restricted_CompFkt_SD, 5 Restricted_CompFkt_Performance red, 6 = No component function, 7 = Init */
|
||||
uint8_t BMS_error_status = 0;
|
||||
/** uint8_t */
|
||||
/** 0 init, 1 closed, 2 open, 3 fault */
|
||||
uint8_t BMS_Kl30c_Status = 0;
|
||||
/** bool */
|
||||
/** true if BMS requests error/warning light */
|
||||
bool BMS_OBD_MIL = 0;
|
||||
bool BMS_error_lamp_req = 0;
|
||||
bool BMS_warning_lamp_req = 0;
|
||||
int32_t BMS_voltage_intermediate_dV = 0;
|
||||
int32_t BMS_voltage_dV = 0;
|
||||
} DATALAYER_INFO_MEB;
|
||||
|
||||
typedef struct {
|
||||
/** uint16_t */
|
||||
/** Values WIP*/
|
||||
|
@ -369,6 +446,7 @@ class DataLayerExtended {
|
|||
DATALAYER_INFO_CELLPOWER cellpower;
|
||||
DATALAYER_INFO_TESLA tesla;
|
||||
DATALAYER_INFO_NISSAN_LEAF nissanleaf;
|
||||
DATALAYER_INFO_MEB meb;
|
||||
DATALAYER_INFO_ZOE_PH2 zoePH2;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,14 +26,14 @@
|
|||
|
||||
// CAN2 defines below
|
||||
|
||||
// DUAL_CAN defines
|
||||
// CAN_ADDON defines
|
||||
#define MCP2515_SCK 12 // SCK input of MCP2515
|
||||
#define MCP2515_MOSI 5 // SDI input of MCP2515
|
||||
#define MCP2515_MISO 34 // SDO output of MCP2515 | Pin 34 is input only, without pullup/down resistors
|
||||
#define MCP2515_CS 18 // CS input of MCP2515
|
||||
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
|
||||
|
||||
// CAN_FD defines
|
||||
// CANFD_ADDON defines
|
||||
#define MCP2517_SCK 17 // SCK input of MCP2517
|
||||
#define MCP2517_SDI 23 // SDI input of MCP2517
|
||||
#define MCP2517_SDO 39 // SDO output of MCP2517
|
||||
|
@ -80,17 +80,17 @@
|
|||
#endif
|
||||
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#ifdef DUAL_CAN
|
||||
#error CHADEMO and DUAL_CAN cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CAN_ADDON
|
||||
#error CHADEMO and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#ifdef DUAL_CAN
|
||||
#error EQUIPMENT_STOP_BUTTON and DUAL_CAN cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CAN_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CAN_FD
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_FD cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CANFD_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CANFD_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage
|
||||
|
|
|
@ -26,14 +26,14 @@
|
|||
|
||||
// CAN2 defines below
|
||||
|
||||
// DUAL_CAN defines
|
||||
// CAN_ADDON defines
|
||||
#define MCP2515_SCK 12 // SCK input of MCP2515
|
||||
#define MCP2515_MOSI 5 // SDI input of MCP2515
|
||||
#define MCP2515_MISO 34 // SDO output of MCP2515 | Pin 34 is input only, without pullup/down resistors
|
||||
#define MCP2515_CS 18 // CS input of MCP2515
|
||||
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
|
||||
|
||||
// CAN_FD defines
|
||||
// CANFD_ADDON defines
|
||||
#define MCP2517_SCK 12 // SCK input of MCP2517
|
||||
#define MCP2517_SDI 5 // SDI input of MCP2517
|
||||
#define MCP2517_SDO 34 // SDO output of MCP2517
|
||||
|
@ -76,17 +76,17 @@
|
|||
#endif
|
||||
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#ifdef DUAL_CAN
|
||||
#error CHADEMO and DUAL_CAN cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CAN_ADDON
|
||||
#error CHADEMO and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#ifdef DUAL_CAN
|
||||
#error EQUIPMENT_STOP_BUTTON and DUAL_CAN cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CAN_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CAN_FD
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_FD cannot coexist due to overlapping GPIO pin usage
|
||||
#ifdef CANFD_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CANFD_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage
|
||||
|
|
|
@ -38,7 +38,7 @@ GPIOs on extra header
|
|||
#define CAN_RX_PIN GPIO_NUM_26
|
||||
// #define CAN_SE_PIN 23 // (No function, GPIO 23 used instead as MCP_SCK)
|
||||
|
||||
// CAN_FD defines
|
||||
// CANFD_ADDON defines
|
||||
#define MCP2517_SCK 17 // SCK input of MCP2517
|
||||
#define MCP2517_SDI 5 // SDI input of MCP2517
|
||||
#define MCP2517_SDO 34 // SDO output of MCP2517
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include "../../../USER_SECRETS.h"
|
||||
#include "../../../USER_SETTINGS.h"
|
||||
#include "../../battery/BATTERIES.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
|
@ -194,9 +195,9 @@ static void publish_common_info(void) {
|
|||
#endif // DOUBLE_BATTERY
|
||||
serializeJson(doc, mqtt_msg);
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
doc.clear();
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
|
@ -292,9 +293,9 @@ static void publish_cell_voltages(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
|
@ -312,9 +313,9 @@ static void publish_cell_voltages(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
|
@ -384,9 +385,9 @@ void publish_events() {
|
|||
|
||||
serializeJson(doc, mqtt_msg);
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
set_event_MQTTpublished(event_handle);
|
||||
}
|
||||
|
@ -402,9 +403,9 @@ void publish_events() {
|
|||
/* If we lose the connection, get it back */
|
||||
static bool reconnect() {
|
||||
// attempt one reconnection
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("Attempting MQTT connection... ");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Attempting MQTT connection... ");
|
||||
#endif // DEBUG_LOG
|
||||
char clientId[64]; // Adjust the size as needed
|
||||
snprintf(clientId, sizeof(clientId), "BatteryEmulatorClient-%s", WiFi.getHostname());
|
||||
// Attempt to connect
|
||||
|
@ -413,19 +414,19 @@ static bool reconnect() {
|
|||
clear_event(EVENT_MQTT_DISCONNECT);
|
||||
set_event(EVENT_MQTT_CONNECT, 0);
|
||||
reconnectAttempts = 0; // Reset attempts on successful connection
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("connected");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("connected");
|
||||
#endif // DEBUG_LOG
|
||||
clear_event(EVENT_MQTT_CONNECT);
|
||||
} else {
|
||||
if (connected_once)
|
||||
set_event(EVENT_MQTT_DISCONNECT, 0);
|
||||
reconnectAttempts++; // Count failed attempts
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("failed, rc=");
|
||||
logging.print(client.state());
|
||||
logging.println(" try again in 5 seconds");
|
||||
#endif // DEBUG_LOG
|
||||
// Wait 5 seconds before retrying
|
||||
}
|
||||
return client.connected();
|
||||
|
@ -449,9 +450,9 @@ void init_mqtt(void) {
|
|||
#endif
|
||||
|
||||
client.setServer(MQTT_SERVER, MQTT_PORT);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("MQTT initialized");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("MQTT initialized");
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
client.setKeepAlive(30); // Increase keepalive to manage network latency better. default is 15
|
||||
|
||||
|
@ -478,8 +479,8 @@ void mqtt_loop(void) {
|
|||
if (reconnect()) {
|
||||
lastReconnectAttempt = 0;
|
||||
} else if (reconnectAttempts >= maxReconnectAttempts) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Too many failed reconnect attempts, restarting client.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Too many failed reconnect attempts, restarting client.");
|
||||
#endif
|
||||
client.disconnect(); // Force close the MQTT client connection
|
||||
reconnectAttempts = 0; // Reset attempts to avoid infinite loop
|
||||
|
|
|
@ -97,7 +97,7 @@ void update_machineryprotection() {
|
|||
clear_event(EVENT_SOH_LOW);
|
||||
}
|
||||
|
||||
#if !defined(PYLON_BATTERY) && !defined(RENAULT_TWIZY_BATTERY)
|
||||
#ifdef NISSAN_LEAF_BATTERY
|
||||
// Check if SOC% is plausible
|
||||
if (datalayer.battery.status.voltage_dV >
|
||||
(datalayer.battery.info.max_design_voltage_dV -
|
||||
|
@ -108,10 +108,11 @@ void update_machineryprotection() {
|
|||
clear_event(EVENT_SOC_PLAUSIBILITY_ERROR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif //NISSAN_LEAF_BATTERY
|
||||
|
||||
// Check diff between highest and lowest cell
|
||||
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
|
||||
cell_deviation_mV =
|
||||
std::abs(datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
|
||||
if (cell_deviation_mV > datalayer.battery.info.max_cell_voltage_deviation_mV) {
|
||||
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
|
||||
} else {
|
||||
|
|
|
@ -118,15 +118,15 @@ void init_events(void) {
|
|||
|
||||
// Push changes to eeprom
|
||||
EEPROM.commit();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("EEPROM wasn't ready");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("EEPROM wasn't ready");
|
||||
#endif
|
||||
} else {
|
||||
events.event_log_head_index = EEPROM.readUShort(EE_EVENT_LOG_HEAD_INDEX_ADDRESS);
|
||||
events.event_log_tail_index = EEPROM.readUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("EEPROM was initialized for event logging");
|
||||
Serial.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("EEPROM was initialized for event logging");
|
||||
logging.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||
#endif
|
||||
print_event_log();
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ void init_events(void) {
|
|||
events.entries[EVENT_CANMCP_INIT_FAILURE].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_CANFD_BUFFER_FULL].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_CAN_OVERRUN].level = EVENT_LEVEL_INFO;
|
||||
events.entries[EVENT_CANFD_RX_OVERRUN].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_CAN_RX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||
events.entries[EVENT_CAN2_RX_FAILURE].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_CANFD_RX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||
|
@ -190,6 +191,7 @@ void init_events(void) {
|
|||
events.entries[EVENT_DUMMY_DEBUG].level = EVENT_LEVEL_DEBUG;
|
||||
events.entries[EVENT_DUMMY_WARNING].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_DUMMY_ERROR].level = EVENT_LEVEL_ERROR;
|
||||
events.entries[EVENT_PERSISTENT_SAVE_INFO].level = EVENT_LEVEL_INFO;
|
||||
events.entries[EVENT_SERIAL_RX_WARNING].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_SERIAL_RX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||
events.entries[EVENT_SERIAL_TX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||
|
@ -270,6 +272,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
|||
return "CAN-FD buffer overflowed. Some CAN messages were not sent. Contact developers.";
|
||||
case EVENT_CAN_OVERRUN:
|
||||
return "CAN message failed to send within defined time. Contact developers, CPU load might be too high.";
|
||||
case EVENT_CANFD_RX_OVERRUN:
|
||||
return "CAN-FD failed to receive all messages from CAN bus. Contact developers, CPU load might be too high.";
|
||||
case EVENT_CAN_RX_FAILURE:
|
||||
return "No CAN communication detected for 60s. Shutting down battery control.";
|
||||
case EVENT_CAN2_RX_FAILURE:
|
||||
|
@ -365,6 +369,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
|||
return "The dummy warning event was set!"; // Don't change this event message!
|
||||
case EVENT_DUMMY_ERROR:
|
||||
return "The dummy error event was set!"; // Don't change this event message!
|
||||
case EVENT_PERSISTENT_SAVE_INFO:
|
||||
return "Info: Failed to save user settings. Namespace full?";
|
||||
case EVENT_SERIAL_RX_WARNING:
|
||||
return "Error in serial function: No data received for some time, see data for minutes";
|
||||
case EVENT_SERIAL_RX_FAILURE:
|
||||
|
@ -465,6 +471,10 @@ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
|
|||
if (events.entries[event].log) {
|
||||
log_event(event, events.entries[event].millisrolloverCount, events.entries[event].timestamp, data);
|
||||
}
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Event: ");
|
||||
logging.println(get_event_message_string(event));
|
||||
#endif
|
||||
}
|
||||
|
||||
// We should set the event, update event info
|
||||
|
@ -478,10 +488,6 @@ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
|
|||
events.level = max(events.level, events.entries[event].level);
|
||||
|
||||
update_bms_status();
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println(get_event_message_string(event));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void update_bms_status(void) {
|
||||
|
@ -555,8 +561,8 @@ static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint3
|
|||
// Store the new indices
|
||||
EEPROM.writeUShort(EE_EVENT_LOG_HEAD_INDEX_ADDRESS, events.event_log_head_index);
|
||||
EEPROM.writeUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS, events.event_log_tail_index);
|
||||
//Serial.println("Wrote event " + String(event) + " to " + String(entry_address));
|
||||
//Serial.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||
//logging.println("Wrote event " + String(event) + " to " + String(entry_address));
|
||||
//logging.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||
|
||||
// We don't need the exact number, it's just for deciding to store or not
|
||||
events.nof_logged_events += (events.nof_logged_events < 255) ? 1 : 0;
|
||||
|
@ -565,8 +571,8 @@ static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint3
|
|||
static void print_event_log(void) {
|
||||
// If the head actually points to the tail, the log is probably blank
|
||||
if (events.event_log_head_index == events.event_log_tail_index) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("No events in log");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("No events in log");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -582,9 +588,9 @@ static void print_event_log(void) {
|
|||
// The entry is a blank that has been left behind somehow
|
||||
continue;
|
||||
}
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Event: " + String(get_event_enum_string(entry.event)) + ", data: " + String(entry.data) +
|
||||
", time: " + String(entry.timestamp));
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Event: " + String(get_event_enum_string(entry.event)) + ", data: " + String(entry.data) +
|
||||
", time: " + String(entry.timestamp));
|
||||
#endif
|
||||
if (index == events.event_log_head_index) {
|
||||
break;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// #define INCLUDE_EVENTS_TEST // Enable to run an event test loop, see events_test_on_target.cpp
|
||||
|
||||
#define EE_MAGIC_HEADER_VALUE 0x0017 // 0x0000 to 0xFFFF
|
||||
#define EE_MAGIC_HEADER_VALUE 0x0018 // 0x0000 to 0xFFFF
|
||||
|
||||
#define GENERATE_ENUM(ENUM) ENUM,
|
||||
#define GENERATE_STRING(STRING) #STRING,
|
||||
|
@ -30,6 +30,7 @@
|
|||
XX(EVENT_CANMCP_INIT_FAILURE) \
|
||||
XX(EVENT_CANFD_BUFFER_FULL) \
|
||||
XX(EVENT_CAN_OVERRUN) \
|
||||
XX(EVENT_CANFD_RX_OVERRUN) \
|
||||
XX(EVENT_CAN_RX_FAILURE) \
|
||||
XX(EVENT_CAN2_RX_FAILURE) \
|
||||
XX(EVENT_CANFD_RX_FAILURE) \
|
||||
|
@ -78,6 +79,7 @@
|
|||
XX(EVENT_DUMMY_DEBUG) \
|
||||
XX(EVENT_DUMMY_WARNING) \
|
||||
XX(EVENT_DUMMY_ERROR) \
|
||||
XX(EVENT_PERSISTENT_SAVE_INFO) \
|
||||
XX(EVENT_SERIAL_RX_WARNING) \
|
||||
XX(EVENT_SERIAL_RX_FAILURE) \
|
||||
XX(EVENT_SERIAL_TX_FAILURE) \
|
||||
|
|
|
@ -25,9 +25,9 @@ void run_sequence_on_target(void) {
|
|||
case ETOT_INIT:
|
||||
timer.set_interval(10000);
|
||||
events_test_state = ETOT_FIRST_WAIT;
|
||||
Serial.println("Events test: initialized");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test: initialized");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
break;
|
||||
case ETOT_FIRST_WAIT:
|
||||
if (timer.elapsed()) {
|
||||
|
@ -35,9 +35,9 @@ void run_sequence_on_target(void) {
|
|||
events_test_state = ETOT_INFO;
|
||||
set_event(EVENT_DUMMY_INFO, 123);
|
||||
set_event(EVENT_DUMMY_INFO, 234); // 234 should show, occurrence 1
|
||||
Serial.println("Events test: info event set, data: 234");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test: info event set, data: 234");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_INFO:
|
||||
|
@ -45,9 +45,9 @@ void run_sequence_on_target(void) {
|
|||
timer.set_interval(8000);
|
||||
clear_event(EVENT_DUMMY_INFO);
|
||||
events_test_state = ETOT_INFO_CLEAR;
|
||||
Serial.println("Events test : info event cleared");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : info event cleared");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_INFO_CLEAR:
|
||||
|
@ -56,9 +56,9 @@ void run_sequence_on_target(void) {
|
|||
events_test_state = ETOT_DEBUG;
|
||||
set_event(EVENT_DUMMY_DEBUG, 111);
|
||||
set_event(EVENT_DUMMY_DEBUG, 222); // 222 should show, occurrence 1
|
||||
Serial.println("Events test : debug event set, data: 222");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : debug event set, data: 222");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_DEBUG:
|
||||
|
@ -66,9 +66,9 @@ void run_sequence_on_target(void) {
|
|||
timer.set_interval(8000);
|
||||
clear_event(EVENT_DUMMY_DEBUG);
|
||||
events_test_state = ETOT_DEBUG_CLEAR;
|
||||
Serial.println("Events test : info event cleared");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : info event cleared");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_DEBUG_CLEAR:
|
||||
|
@ -77,9 +77,9 @@ void run_sequence_on_target(void) {
|
|||
events_test_state = ETOT_WARNING;
|
||||
set_event(EVENT_DUMMY_WARNING, 234);
|
||||
set_event(EVENT_DUMMY_WARNING, 121); // 121 should show, occurrence 1
|
||||
Serial.println("Events test : warning event set, data: 121");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : warning event set, data: 121");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_WARNING:
|
||||
|
@ -87,9 +87,9 @@ void run_sequence_on_target(void) {
|
|||
timer.set_interval(8000);
|
||||
clear_event(EVENT_DUMMY_WARNING);
|
||||
events_test_state = ETOT_WARNING_CLEAR;
|
||||
Serial.println("Events test : warning event cleared");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : warning event cleared");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_WARNING_CLEAR:
|
||||
|
@ -98,9 +98,9 @@ void run_sequence_on_target(void) {
|
|||
events_test_state = ETOT_ERROR;
|
||||
set_event(EVENT_DUMMY_ERROR, 221);
|
||||
set_event(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
|
||||
Serial.println("Events test : error event set, data: 133");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : error event set, data: 133");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_ERROR:
|
||||
|
@ -108,9 +108,9 @@ void run_sequence_on_target(void) {
|
|||
timer.set_interval(8000);
|
||||
clear_event(EVENT_DUMMY_ERROR);
|
||||
events_test_state = ETOT_ERROR_CLEAR;
|
||||
Serial.println("Events test : error event cleared");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : error event cleared");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_ERROR_CLEAR:
|
||||
|
@ -119,9 +119,9 @@ void run_sequence_on_target(void) {
|
|||
events_test_state = ETOT_ERROR_LATCHED;
|
||||
set_event_latched(EVENT_DUMMY_ERROR, 221);
|
||||
set_event_latched(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
|
||||
Serial.println("Events test : latched error event set, data: 133");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : latched error event set, data: 133");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_ERROR_LATCHED:
|
||||
|
@ -129,9 +129,9 @@ void run_sequence_on_target(void) {
|
|||
timer.set_interval(8000);
|
||||
clear_event(EVENT_DUMMY_ERROR);
|
||||
events_test_state = ETOT_DONE;
|
||||
Serial.println("Events test : latched error event cleared?");
|
||||
Serial.print("datalayer.battery.status.bms_status: ");
|
||||
Serial.println(datalayer.battery.status.bms_status);
|
||||
logging.println("Events test : latched error event cleared?");
|
||||
logging.print("datalayer.battery.status.bms_status: ");
|
||||
logging.println(datalayer.battery.status.bms_status);
|
||||
}
|
||||
break;
|
||||
case ETOT_DONE:
|
||||
|
|
86
Software/src/devboard/utils/logging.cpp
Normal file
86
Software/src/devboard/utils/logging.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include "logging.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
|
||||
size_t Logging::write(const uint8_t* buffer, size_t size) {
|
||||
#ifdef DEBUG_LOG
|
||||
char* message_string = datalayer.system.info.logged_can_messages;
|
||||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
unsigned long currentTime = millis();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
if (Serial.write(*buffer++))
|
||||
n++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
#endif
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (datalayer.system.info.can_logging_active) {
|
||||
return 0;
|
||||
}
|
||||
if (offset + size + 13 > sizeof(datalayer.system.info.logged_can_messages)) {
|
||||
offset = 0;
|
||||
}
|
||||
if (buffer[0] != '\r' && buffer[0] != '\n' &&
|
||||
(offset == 0 || message_string[offset - 1] == '\r' || message_string[offset - 1] == '\n')) {
|
||||
offset += snprintf(message_string + offset, message_string_size - offset - 1, "%8lu.%03lu ", currentTime / 1000,
|
||||
currentTime % 1000);
|
||||
}
|
||||
memcpy(message_string + offset, buffer, size);
|
||||
datalayer.system.info.logged_can_messages_offset = offset + size; // Update offset in buffer
|
||||
return size;
|
||||
#endif // DEBUG_VIA_WEB
|
||||
#endif // DEBUG_LOG
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Logging::printf(const char* fmt, ...) {
|
||||
#ifdef DEBUG_LOG
|
||||
char* message_string = datalayer.system.info.logged_can_messages;
|
||||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
static char buf[128];
|
||||
message_string = buf;
|
||||
offset = 0;
|
||||
message_string_size = sizeof(buf);
|
||||
#endif
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (datalayer.system.info.can_logging_active) {
|
||||
return;
|
||||
}
|
||||
message_string = datalayer.system.info.logged_can_messages;
|
||||
offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
#endif
|
||||
if (offset + 128 > sizeof(datalayer.system.info.logged_can_messages)) {
|
||||
// Not enough space, reset and start from the beginning
|
||||
offset = 0;
|
||||
}
|
||||
unsigned long currentTime = millis();
|
||||
// Add timestamp
|
||||
offset += snprintf(message_string + offset, message_string_size - offset - 1, "%8lu.%03lu ", currentTime / 1000,
|
||||
currentTime % 1000);
|
||||
|
||||
va_list(args);
|
||||
va_start(args, fmt);
|
||||
offset += vsnprintf(message_string + offset, message_string_size - offset - 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (datalayer.system.info.can_logging_active) {
|
||||
size_t size = offset;
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
if (Serial.write(*message_string++))
|
||||
n++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer
|
||||
}
|
||||
#endif // DEBUG_LOG
|
||||
}
|
16
Software/src/devboard/utils/logging.h
Normal file
16
Software/src/devboard/utils/logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef __LOGGING_H__
|
||||
#define __LOGGING_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
|
||||
class Logging : public Print {
|
||||
public:
|
||||
virtual size_t write(const uint8_t* buffer, size_t size);
|
||||
virtual size_t write(uint8_t) { return 0; }
|
||||
void printf(const char* fmt, ...);
|
||||
Logging() {}
|
||||
};
|
||||
|
||||
extern Logging logging;
|
||||
#endif // __LOGGING_H__
|
|
@ -13,7 +13,9 @@ enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
|
|||
#define INTERVAL_10_MS 10
|
||||
#define INTERVAL_20_MS 20
|
||||
#define INTERVAL_30_MS 30
|
||||
#define INTERVAL_40_MS 40
|
||||
#define INTERVAL_50_MS 50
|
||||
#define INTERVAL_70_MS 70
|
||||
#define INTERVAL_100_MS 100
|
||||
#define INTERVAL_200_MS 200
|
||||
#define INTERVAL_250_MS 250
|
||||
|
|
|
@ -437,6 +437,20 @@ String advanced_battery_processor(const String& var) {
|
|||
#ifdef NISSAN_LEAF_BATTERY
|
||||
static const char* LEAFgen[] = {"ZE0", "AZE0", "ZE1"};
|
||||
content += "<h4>LEAF generation: " + String(LEAFgen[datalayer_extended.nissanleaf.LEAF_gen]) + "</h4>";
|
||||
char readableSerialNumber[16]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber,
|
||||
sizeof(datalayer_extended.nissanleaf.BatterySerialNumber));
|
||||
readableSerialNumber[15] = '\0'; // Null terminate the string
|
||||
content += "<h4>Serial number: " + String(readableSerialNumber) + "</h4>";
|
||||
char readablePartNumber[8]; // One extra space for null terminator
|
||||
memcpy(readablePartNumber, datalayer_extended.nissanleaf.BatteryPartNumber,
|
||||
sizeof(datalayer_extended.nissanleaf.BatteryPartNumber));
|
||||
readablePartNumber[7] = '\0'; // Null terminate the string
|
||||
content += "<h4>Part number: " + String(readablePartNumber) + "</h4>";
|
||||
char readableBMSID[9]; // One extra space for null terminator
|
||||
memcpy(readableBMSID, datalayer_extended.nissanleaf.BMSIDcode, sizeof(datalayer_extended.nissanleaf.BMSIDcode));
|
||||
readableBMSID[8] = '\0'; // Null terminate the string
|
||||
content += "<h4>BMS ID: " + String(readableBMSID) + "</h4>";
|
||||
content += "<h4>GIDS: " + String(datalayer_extended.nissanleaf.GIDS) + "</h4>";
|
||||
content += "<h4>Regen kW: " + String(datalayer_extended.nissanleaf.ChargePowerLimit) + "</h4>";
|
||||
content += "<h4>Charge kW: " + String(datalayer_extended.nissanleaf.MaxPowerForCharger) + "</h4>";
|
||||
|
@ -458,6 +472,205 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "<h4>Challenge failed: " + String(datalayer_extended.nissanleaf.challengeFailed) + "</h4>";
|
||||
#endif
|
||||
|
||||
#ifdef MEB_BATTERY
|
||||
content += datalayer_extended.meb.SDSW ? "<h4>Service disconnect switch: Missing!</h4>"
|
||||
: "<h4>Service disconnect switch: OK</h4>";
|
||||
content += datalayer_extended.meb.pilotline ? "<h4>Pilotline: Open!</h4>" : "<h4>Pilotline: OK</h4>";
|
||||
content += datalayer_extended.meb.transportmode ? "<h4>Transportmode: Locked!</h4>" : "<h4>Transportmode: OK</h4>";
|
||||
content += datalayer_extended.meb.shutdown_active ? "<h4>Shutdown: Active!</h4>" : "<h4>Shutdown: No</h4>";
|
||||
content += datalayer_extended.meb.componentprotection ? "<h4>Component protection: Active!</h4>"
|
||||
: "<h4>Component protection: No</h4>";
|
||||
content += "<h4>HVIL status: ";
|
||||
switch (datalayer_extended.meb.HVIL) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open!");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>KL30C status: ";
|
||||
switch (datalayer_extended.meb.BMS_Kl30c_Status) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open!");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS mode: ";
|
||||
switch (datalayer_extended.meb.BMS_mode) {
|
||||
case 0:
|
||||
content += String("HV inactive");
|
||||
break;
|
||||
case 1:
|
||||
content += String("HV active");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Balancing");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Extern charging");
|
||||
break;
|
||||
case 4:
|
||||
content += String("AC charging");
|
||||
break;
|
||||
case 5:
|
||||
content += String("Battery error");
|
||||
break;
|
||||
case 6:
|
||||
content += String("DC charging");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Init");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>Diagnostic: ";
|
||||
switch (datalayer_extended.meb.battery_diagnostic) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Battery display");
|
||||
break;
|
||||
case 4:
|
||||
content += String("Battery display OK");
|
||||
break;
|
||||
case 6:
|
||||
content += String("Battery display check");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>HV line status: ";
|
||||
switch (datalayer_extended.meb.status_HV_line) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("No open HV line detected");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open HV line");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("? ") + String(datalayer_extended.meb.status_HV_line);
|
||||
}
|
||||
content += "</h4><h4>Warning support: ";
|
||||
switch (datalayer_extended.meb.warning_support) {
|
||||
case 0:
|
||||
content += String("OK");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Not OK");
|
||||
break;
|
||||
case 6:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>Interm. Voltage (" + String(datalayer_extended.meb.BMS_voltage_intermediate_dV / 10.0, 1) +
|
||||
"V) status: ";
|
||||
switch (datalayer_extended.meb.BMS_status_voltage_free) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("BMS interm circuit voltage free (U<20V)");
|
||||
break;
|
||||
case 2:
|
||||
content += String("BMS interm circuit not voltage free (U >= 25V)");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Error");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS error status: ";
|
||||
switch (datalayer_extended.meb.BMS_error_status) {
|
||||
case 0:
|
||||
content += String("Component IO");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Iso Error 1");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Iso Error 2");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Interlock");
|
||||
break;
|
||||
case 4:
|
||||
content += String("SD");
|
||||
break;
|
||||
case 5:
|
||||
content += String("Performance red");
|
||||
break;
|
||||
case 6:
|
||||
content += String("No component function");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Init");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV / 10.0, 1) + "</h4>";
|
||||
content += datalayer_extended.meb.BMS_OBD_MIL ? "<h4>OBD MIL: ON!</h4>" : "<h4>OBD MIL: Off</h4>";
|
||||
content +=
|
||||
datalayer_extended.meb.BMS_error_lamp_req ? "<h4>Red error lamp: ON!</h4>" : "<h4>Red error lamp: Off</h4>";
|
||||
content += datalayer_extended.meb.BMS_warning_lamp_req ? "<h4>Yellow warning lamp: ON!</h4>"
|
||||
: "<h4>Yellow warning lamp: Off</h4>";
|
||||
content += "<h4>Isolation resistance: " + String(datalayer_extended.meb.isolation_resistance) + " kOhm</h4>";
|
||||
content +=
|
||||
datalayer_extended.meb.battery_heating ? "<h4>Battery heating: Active!</h4>" : "<h4>Battery heating: Off</h4>";
|
||||
const char* rt_enum[] = {"No", "Error level 1", "Error level 2", "Error level 3"};
|
||||
content += "<h4>Overcurrent: " + String(rt_enum[datalayer_extended.meb.rt_overcurrent]) + "</h4>";
|
||||
content += "<h4>CAN fault: " + String(rt_enum[datalayer_extended.meb.rt_CAN_fault]) + "</h4>";
|
||||
content += "<h4>Overcharged: " + String(rt_enum[datalayer_extended.meb.rt_overcharge]) + "</h4>";
|
||||
content += "<h4>SOC too high: " + String(rt_enum[datalayer_extended.meb.rt_SOC_high]) + "</h4>";
|
||||
content += "<h4>SOC too low: " + String(rt_enum[datalayer_extended.meb.rt_SOC_low]) + "</h4>";
|
||||
content += "<h4>SOC jumping: " + String(rt_enum[datalayer_extended.meb.rt_SOC_jumping]) + "</h4>";
|
||||
content += "<h4>Temp difference: " + String(rt_enum[datalayer_extended.meb.rt_temp_difference]) + "</h4>";
|
||||
content += "<h4>Cell overtemp: " + String(rt_enum[datalayer_extended.meb.rt_cell_overtemp]) + "</h4>";
|
||||
content += "<h4>Cell undertemp: " + String(rt_enum[datalayer_extended.meb.rt_cell_undertemp]) + "</h4>";
|
||||
content += "<h4>Battery overvoltage: " + String(rt_enum[datalayer_extended.meb.rt_battery_overvolt]) + "</h4>";
|
||||
content += "<h4>Battery undervoltage: " + String(rt_enum[datalayer_extended.meb.rt_battery_undervol]) + "</h4>";
|
||||
content += "<h4>Cell overvoltage: " + String(rt_enum[datalayer_extended.meb.rt_cell_overvolt]) + "</h4>";
|
||||
content += "<h4>Cell undervoltage: " + String(rt_enum[datalayer_extended.meb.rt_cell_undervol]) + "</h4>";
|
||||
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance]) + "</h4>";
|
||||
content += "<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized]) + "</h4>";
|
||||
#endif //MEB_BATTERY
|
||||
|
||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||
content += "<h4>soc: " + String(datalayer_extended.zoePH2.battery_soc) + "</h4>";
|
||||
content += "<h4>usable soc: " + String(datalayer_extended.zoePH2.battery_usable_soc) + "</h4>";
|
||||
|
@ -507,7 +720,7 @@ String advanced_battery_processor(const String& var) {
|
|||
|
||||
#if !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_BATTERY) && !defined(TESLA_BATTERY) && \
|
||||
!defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && !defined(BYD_ATTO_3_BATTERY) && \
|
||||
!defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS)
|
||||
!defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && !defined(MEB_BATTERY) // Only the listed types have extra info
|
||||
content += "No extra information available for this battery type";
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
String can_logger_processor(const String& var) {
|
||||
if (var == "X") {
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
datalayer.system.info.logged_can_messages_offset = 0;
|
||||
datalayer.system.info.logged_can_messages[0] = '\0';
|
||||
}
|
||||
datalayer.system.info.can_logging_active =
|
||||
true; // Signal to main loop that we should log messages. Disabled by default for performance reasons
|
||||
String content = "";
|
||||
|
@ -19,7 +23,7 @@ String can_logger_processor(const String& var) {
|
|||
"monospace; }";
|
||||
content += "</style>";
|
||||
content += "<button onclick='refreshPage()'>Refresh data</button> ";
|
||||
content += "<button onclick='exportLogs()'>Export to .txt</button> ";
|
||||
content += "<button onclick='exportLog()'>Export to .txt</button> ";
|
||||
content += "<button onclick='stopLoggingAndGoToMainPage()'>Back to main page</button>";
|
||||
|
||||
// Start a new block for the CAN messages
|
||||
|
@ -47,9 +51,9 @@ String can_logger_processor(const String& var) {
|
|||
// Add JavaScript for navigation
|
||||
content += "<script>";
|
||||
content += "function refreshPage(){ location.reload(true); }";
|
||||
content += "function exportLogs() { window.location.href = '/export_logs'; }";
|
||||
content += "function exportLog() { window.location.href = '/export_can_log'; }";
|
||||
content += "function stopLoggingAndGoToMainPage() {";
|
||||
content += " fetch('/stop_logging').then(() => window.location.href = '/');";
|
||||
content += " fetch('/stop_can_logging').then(() => window.location.href = '/');";
|
||||
content += "}";
|
||||
content += "</script>";
|
||||
return content;
|
||||
|
|
36
Software/src/devboard/webserver/debug_logging_html.cpp
Normal file
36
Software/src/devboard/webserver/debug_logging_html.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "debug_logging_html.h"
|
||||
#include <Arduino.h>
|
||||
#include "../../datalayer/datalayer.h"
|
||||
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
String debug_logger_processor(const String& var) {
|
||||
String content = "";
|
||||
// Page format
|
||||
content += "<style>";
|
||||
content += "body { background-color: black; color: white; font-family: Arial, sans-serif; }";
|
||||
content +=
|
||||
"button { background-color: #505E67; color: white; border: none; padding: 10px 20px; margin-bottom: 20px; "
|
||||
"cursor: pointer; border-radius: 10px; }";
|
||||
content += "button:hover { background-color: #3A4A52; }";
|
||||
content +=
|
||||
".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: "
|
||||
"monospace; }";
|
||||
content += "</style>";
|
||||
content += "<button onclick='refreshPage()'>Refresh data</button> ";
|
||||
content += "<button onclick='exportLog()'>Export to .txt</button> ";
|
||||
content += "<button onclick='goToMainPage()'>Back to main page</button>";
|
||||
|
||||
// Start a new block for the debug log messages
|
||||
content += "<PRE style='text-align: left'>";
|
||||
content += String(datalayer.system.info.logged_can_messages);
|
||||
content += "</PRE>";
|
||||
|
||||
// Add JavaScript for navigation
|
||||
content += "<script>";
|
||||
content += "function refreshPage(){ location.reload(true); }";
|
||||
content += "function exportLog() { window.location.href = '/export_log'; }";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
return content;
|
||||
}
|
||||
#endif // DEBUG_VIA_WEB
|
16
Software/src/devboard/webserver/debug_logging_html.h
Normal file
16
Software/src/devboard/webserver/debug_logging_html.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef DEBUGLOGGER_H
|
||||
#define DEBUGLOGGER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Replaces placeholder with content section in web page
|
||||
*
|
||||
* @param[in] var
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String debug_logger_processor(const String& var);
|
||||
|
||||
#endif
|
|
@ -39,11 +39,11 @@ String events_processor(const String& var) {
|
|||
for (const auto& event : order_events) {
|
||||
EVENTS_ENUM_TYPE event_handle = event.event_handle;
|
||||
event_pointer = event.event_pointer;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Event: " + String(get_event_enum_string(event_handle)) +
|
||||
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
||||
" data: " + String(event_pointer->data) +
|
||||
" level: " + String(get_event_level_string(event_handle)));
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Showing Event: " + String(get_event_enum_string(event_handle)) +
|
||||
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
||||
" data: " + String(event_pointer->data) +
|
||||
" level: " + String(get_event_level_string(event_handle)));
|
||||
#endif
|
||||
content.concat("<div class='event'>");
|
||||
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
|
||||
|
@ -60,8 +60,8 @@ String events_processor(const String& var) {
|
|||
order_events.clear();
|
||||
content.concat(FPSTR(EVENTS_HTML_END));
|
||||
return content;
|
||||
return String();
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
/* Script for displaying event log before it gets minified
|
||||
|
|
|
@ -67,6 +67,21 @@ String settings_processor(const String& var) {
|
|||
content += "<h4 style='color: white;'>Max discharge speed: " +
|
||||
String(datalayer.battery.settings.max_user_set_discharge_dA / 10.0, 1) +
|
||||
" A </span> <button onclick='editMaxDischargeA()'>Edit</button></h4>";
|
||||
content += "<h4 style='color: white;'>Manual charge voltage limits: <span id='BATTERY_USE_VOLTAGE_LIMITS'>" +
|
||||
String(datalayer.battery.settings.user_set_voltage_limits_active
|
||||
? "<span>✓</span>"
|
||||
: "<span style='color: red;'>✕</span>") +
|
||||
"</span> <button onclick='editUseVoltageLimit()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " +
|
||||
String(datalayer.battery.settings.user_set_voltage_limits_active ? "white" : "darkgrey") +
|
||||
";'>Target charge voltage: " + String(datalayer.battery.settings.max_user_set_charge_voltage_dV / 10.0, 1) +
|
||||
" V </span> <button onclick='editMaxChargeVoltage()'>Edit</button></h4>";
|
||||
content += "<h4 style='color: " +
|
||||
String(datalayer.battery.settings.user_set_voltage_limits_active ? "white" : "darkgrey") +
|
||||
";'>Target discharge voltage: " +
|
||||
String(datalayer.battery.settings.max_user_set_discharge_voltage_dV / 10.0, 1) +
|
||||
" V </span> <button onclick='editMaxDischargeVoltage()'>Edit</button></h4>";
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
|
@ -130,7 +145,9 @@ String settings_processor(const String& var) {
|
|||
"updateBatterySize?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 1 "
|
||||
"and 120000.');}}}";
|
||||
content +=
|
||||
"function editUseScaledSOC(){var value=prompt('Should SOC% be scaled? (0 = No, 1 = "
|
||||
"function editUseScaledSOC(){var value=prompt('Extends battery life by rescaling the SOC within the configured "
|
||||
"minimum "
|
||||
"and maximum percentage. Should SOC scaling be applied? (0 = No, 1 = "
|
||||
"Yes):');if(value!==null){if(value==0||value==1){var xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateUseScaledSOC?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
|
||||
|
@ -161,6 +178,33 @@ String settings_processor(const String& var) {
|
|||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateMaxDischargeA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
|
||||
"and 1000.0');}}}";
|
||||
content +=
|
||||
"function editUseVoltageLimit(){var value=prompt('Enable this option to manually restrict charge/discharge to "
|
||||
"a specific voltage set below."
|
||||
"If disabled the emulator automatically determines this based on battery limits. Restrict manually? (0 = No, 1 "
|
||||
"= Yes)"
|
||||
":');if(value!==null){if(value==0||value==1){var xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateUseVoltageLimit?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between "
|
||||
"0 "
|
||||
"and 1.');}}}";
|
||||
content +=
|
||||
"function editMaxChargeVoltage(){var value=prompt('Some inverters needs to be artificially limited. Enter new "
|
||||
"voltage setpoint batttery should charge to (0-1000.0):');if(value!==null){if(value>=0&&value<=1000){var "
|
||||
"xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateMaxChargeVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||
"between 0 "
|
||||
"and 1000.0');}}}";
|
||||
content +=
|
||||
"function editMaxDischargeVoltage(){var value=prompt('Some inverters needs to be artificially limited. Enter "
|
||||
"new "
|
||||
"voltage setpoint batttery should discharge to (0-1000.0):');if(value!==null){if(value>=0&&value<=1000){var "
|
||||
"xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateMaxDischargeVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||
"between 0 "
|
||||
"and 1000.0');}}}";
|
||||
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
content +=
|
||||
|
@ -227,7 +271,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
|||
#endif
|
||||
case CAN_ADDON_MCP2515:
|
||||
return "Add-on CAN via GPIO MCP2515";
|
||||
case CAN_ADDON_FD_MCP2518:
|
||||
case CANFD_ADDON_MCP2518:
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
return "Add-on CAN-FD via GPIO MCP2518 (Classic CAN)";
|
||||
#else
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "webserver.h"
|
||||
#include <Preferences.h>
|
||||
#include <ctime>
|
||||
#include "../../../USER_SECRETS.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../datalayer/datalayer_extended.h"
|
||||
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||
|
@ -17,6 +18,7 @@ unsigned long ota_progress_millis = 0;
|
|||
#include "advanced_battery_html.h"
|
||||
#include "can_logging_html.h"
|
||||
#include "cellmonitor_html.h"
|
||||
#include "debug_logging_html.h"
|
||||
#include "events_html.h"
|
||||
#include "index_html.cpp"
|
||||
#include "settings_html.h"
|
||||
|
@ -63,14 +65,21 @@ void init_webserver() {
|
|||
request->send_P(200, "text/html", index_html, can_logger_processor);
|
||||
});
|
||||
|
||||
// Define the handler to stop logging
|
||||
server.on("/stop_logging", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
// Route for going to debug logging web page
|
||||
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send_P(200, "text/html", index_html, debug_logger_processor);
|
||||
});
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
// Define the handler to stop can logging
|
||||
server.on("/stop_can_logging", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
datalayer.system.info.can_logging_active = false;
|
||||
request->send_P(200, "text/plain", "Logging stopped");
|
||||
});
|
||||
|
||||
// Define the handler to export logs
|
||||
server.on("/export_logs", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
// Define the handler to export can log
|
||||
server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
|
@ -96,6 +105,33 @@ void init_webserver() {
|
|||
request->send(response);
|
||||
});
|
||||
|
||||
// Define the handler to export debug log
|
||||
server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
}
|
||||
|
||||
// Get the current time
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
// Ensure time retrieval was successful
|
||||
char filename[32];
|
||||
if (strftime(filename, sizeof(filename), "log_%H-%M-%S.txt", &timeinfo)) {
|
||||
// Valid filename created
|
||||
} else {
|
||||
// Fallback filename if automatic timestamping failed
|
||||
strcpy(filename, "battery_emulator_log.txt");
|
||||
}
|
||||
|
||||
// Use request->send with dynamic headers
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs);
|
||||
response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\"");
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// Route for going to cellmonitor web page
|
||||
server.on("/cellmonitor", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
|
@ -131,7 +167,7 @@ void init_webserver() {
|
|||
String value = request->getParam("value")->value();
|
||||
if (value.length() <= 63) { // Check if SSID is within the allowable length
|
||||
ssid = value.c_str();
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "SSID must be 63 characters or less");
|
||||
|
@ -148,7 +184,7 @@ void init_webserver() {
|
|||
String value = request->getParam("value")->value();
|
||||
if (value.length() > 8) { // Check if password is within the allowable length
|
||||
password = value.c_str();
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Password must be atleast 8 characters");
|
||||
|
@ -165,7 +201,7 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.info.total_capacity_Wh = value.toInt();
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -179,7 +215,7 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.soc_scaling_active = value.toInt();
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -193,7 +229,7 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.max_percentage = static_cast<uint16_t>(value.toFloat() * 100);
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -237,7 +273,7 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.min_percentage = static_cast<uint16_t>(value.toFloat() * 100);
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -251,7 +287,7 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.max_user_set_charge_dA = static_cast<uint16_t>(value.toFloat() * 10);
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -265,7 +301,49 @@ void init_webserver() {
|
|||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.max_user_set_discharge_dA = static_cast<uint16_t>(value.toFloat() * 10);
|
||||
storeSettings();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
|
||||
// Route for editing BATTERY_USE_VOLTAGE_LIMITS
|
||||
server.on("/updateUseVoltageLimit", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
return request->requestAuthentication();
|
||||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.user_set_voltage_limits_active = value.toInt();
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
|
||||
// Route for editing MaxChargeVoltage
|
||||
server.on("/updateMaxChargeVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
return request->requestAuthentication();
|
||||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.max_user_set_charge_voltage_dV = static_cast<uint16_t>(value.toFloat() * 10);
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
|
||||
// Route for editing MaxDischargeVoltage
|
||||
server.on("/updateMaxDischargeVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
return request->requestAuthentication();
|
||||
if (request->hasParam("value")) {
|
||||
String value = request->getParam("value")->value();
|
||||
datalayer.battery.settings.max_user_set_discharge_voltage_dV = static_cast<uint16_t>(value.toFloat() * 10);
|
||||
store_settings();
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
|
@ -493,14 +571,15 @@ String processor(const String& var) {
|
|||
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
|
||||
// Show version number
|
||||
content += "<h4>Software: " + String(version_number) + "</h4>";
|
||||
content += "<h4>Software: " + String(version_number);
|
||||
// Show hardware used:
|
||||
#ifdef HW_LILYGO
|
||||
content += "<h4>Hardware: LilyGo T-CAN485</h4>";
|
||||
content += " Hardware: LilyGo T-CAN485";
|
||||
#endif // HW_LILYGO
|
||||
#ifdef HW_STARK
|
||||
content += "<h4>Hardware: Stark CMR Module</h4>";
|
||||
content += " Hardware: Stark CMR Module";
|
||||
#endif // HW_STARK
|
||||
content += "</h4>";
|
||||
content += "<h4>Uptime: " + uptime_formatter::getUptime() + "</h4>";
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
// Load information
|
||||
|
@ -524,11 +603,14 @@ String processor(const String& var) {
|
|||
|
||||
wl_status_t status = WiFi.status();
|
||||
// Display ssid of network connected to and, if connected to the WiFi, its own IP
|
||||
content += "<h4>SSID: " + String(ssid.c_str()) + "</h4>";
|
||||
content += "<h4>SSID: " + String(ssid.c_str());
|
||||
if (status == WL_CONNECTED) {
|
||||
// Get and display the signal strength (RSSI) and channel
|
||||
content += " RSSI:" + String(WiFi.RSSI()) + " dBm Ch: " + String(WiFi.channel());
|
||||
}
|
||||
content += "</h4>";
|
||||
if (status == WL_CONNECTED) {
|
||||
content += "<h4>IP: " + WiFi.localIP().toString() + "</h4>";
|
||||
// Get and display the signal strength (RSSI) and channel
|
||||
content += "<h4>Signal strength: " + String(WiFi.RSSI()) + " dBm, at channel " + String(WiFi.channel()) + "</h4>";
|
||||
} else {
|
||||
content += "<h4>Wifi state: " + getConnectResultString(status) + "</h4>";
|
||||
}
|
||||
|
@ -650,13 +732,30 @@ String processor(const String& var) {
|
|||
}
|
||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
content += "<h4>System status: UPDATING </h4>";
|
||||
} else {
|
||||
content += "<h4>System status: FAULT </h4>";
|
||||
|
||||
content += "<h4>System status: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
case ACTIVE:
|
||||
content += String("OK");
|
||||
break;
|
||||
case UPDATING:
|
||||
content += String("UPDATING");
|
||||
break;
|
||||
case FAULT:
|
||||
content += String("FAULT");
|
||||
break;
|
||||
case INACTIVE:
|
||||
content += String("INACTIVE");
|
||||
break;
|
||||
case STANDBY:
|
||||
content += String("STANDBY");
|
||||
break;
|
||||
default:
|
||||
content += String("??");
|
||||
break;
|
||||
}
|
||||
content += "</h4>";
|
||||
|
||||
if (datalayer.battery.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery.status.current_dA < 0) {
|
||||
|
@ -685,7 +784,7 @@ String processor(const String& var) {
|
|||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
|
@ -693,13 +792,21 @@ String processor(const String& var) {
|
|||
}
|
||||
content += "</h4>";
|
||||
|
||||
content += "<h4>Pre Charge: ";
|
||||
if (digitalRead(PRECHARGE_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
content += "<h4>Precharge: (";
|
||||
content += PRECHARGE_TIME_MS;
|
||||
content += " ms) Cont. Neg.: ";
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (datalayer.system.status.contactors_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += " Cont. Neg.: ";
|
||||
|
||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
|
@ -712,6 +819,7 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
#endif
|
||||
|
||||
|
@ -816,7 +924,7 @@ String processor(const String& var) {
|
|||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
|
@ -824,13 +932,19 @@ String processor(const String& var) {
|
|||
}
|
||||
content += "</h4>";
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
content += "<h4>Pre Charge: ";
|
||||
if (digitalRead(SECOND_PRECHARGE_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += " Cont. Neg.: ";
|
||||
|
||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
|
@ -843,6 +957,7 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
#endif // CONTACTOR_CONTROL
|
||||
|
@ -919,6 +1034,9 @@ String processor(const String& var) {
|
|||
content += "<button onclick='Settings()'>Change Settings</button> ";
|
||||
content += "<button onclick='Advanced()'>More Battery Info</button> ";
|
||||
content += "<button onclick='CANlog()'>CAN logger</button> ";
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
content += "<button onclick='Log()'>Log</button> ";
|
||||
#endif // DEBUG_VIA_WEB
|
||||
content += "<button onclick='Cellmon()'>Cellmonitor</button> ";
|
||||
content += "<button onclick='Events()'>Events</button> ";
|
||||
content += "<button onclick='askReboot()'>Reboot Emulator</button>";
|
||||
|
@ -944,6 +1062,7 @@ String processor(const String& var) {
|
|||
content += "function Settings() { window.location.href = '/settings'; }";
|
||||
content += "function Advanced() { window.location.href = '/advanced'; }";
|
||||
content += "function CANlog() { window.location.href = '/canlog'; }";
|
||||
content += "function Log() { window.location.href = '/log'; }";
|
||||
content += "function Events() { window.location.href = '/events'; }";
|
||||
content +=
|
||||
"function askReboot() { if (window.confirm('Are you sure you want to reboot the emulator? NOTE: If "
|
||||
|
@ -1004,9 +1123,9 @@ void onOTAProgress(size_t current, size_t final) {
|
|||
// Log every 1 second
|
||||
if (millis() - ota_progress_millis > 1000) {
|
||||
ota_progress_millis = millis();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||
#endif // DEBUG_LOG
|
||||
// Reset the "watchdog"
|
||||
ota_timeout_timer.reset();
|
||||
}
|
||||
|
@ -1023,13 +1142,13 @@ void onOTAEnd(bool success) {
|
|||
// Max Charge/Discharge = 0; CAN = stop; contactors = open
|
||||
setBatteryPause(true, true, true, false);
|
||||
// a reboot will be done by the OTA library. no need to do anything here
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("OTA update finished successfully!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("OTA update finished successfully!");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("There was an error during OTA update!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("There was an error during OTA update!");
|
||||
#endif // DEBUG_LOG
|
||||
//try to Resume the battery pause and CAN communication
|
||||
setBatteryPause(false, false);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "../../include.h"
|
||||
#include "../../lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h"
|
||||
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
|
||||
#include "../../lib/me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../lib/mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../lib/me-no-dev-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
|
||||
|
@ -104,7 +104,7 @@ void onOTAEnd(bool success);
|
|||
template <typename T>
|
||||
String formatPowerValue(String label, T value, String unit, int precision, String color = "white");
|
||||
|
||||
extern void storeSettings();
|
||||
extern void store_settings();
|
||||
|
||||
void ota_monitor();
|
||||
|
||||
|
|
|
@ -72,28 +72,28 @@ void wifi_monitor() {
|
|||
// Increase the current check interval if it's not at the maximum
|
||||
if (current_check_interval + STEP_WIFI_CHECK_INTERVAL <= MAX_STEP_WIFI_CHECK_INTERVAL)
|
||||
current_check_interval += STEP_WIFI_CHECK_INTERVAL;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi not connected, attempting to reconnect...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi not connected, attempting to reconnect...");
|
||||
#endif
|
||||
// Try WiFi.reconnect() if it was successfully connected at least once
|
||||
if (hasConnectedBefore) {
|
||||
lastReconnectAttempt = millis(); // Reset reconnection attempt timer
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi reconnect attempt...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt...");
|
||||
#endif
|
||||
if (WiFi.reconnect()) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi reconnect attempt sucess...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt sucess...");
|
||||
#endif
|
||||
reconnectAttempts = 0; // Reset the attempt counter on successful reconnect
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi reconnect attempt error...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt error...");
|
||||
#endif
|
||||
reconnectAttempts++;
|
||||
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Failed to reconnect multiple times, forcing a full connection attempt...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Failed to reconnect multiple times, forcing a full connection attempt...");
|
||||
#endif
|
||||
FullReconnectToWiFi();
|
||||
}
|
||||
|
@ -101,8 +101,8 @@ void wifi_monitor() {
|
|||
} else {
|
||||
// If no previous connection, force a full connection attempt
|
||||
if (currentMillis - lastReconnectAttempt > current_full_reconnect_interval) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("No previous OK connection, force a full connection attempt...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("No previous OK connection, force a full connection attempt...");
|
||||
#endif
|
||||
FullReconnectToWiFi();
|
||||
}
|
||||
|
@ -127,13 +127,13 @@ static void FullReconnectToWiFi() {
|
|||
static void connectToWiFi() {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
lastReconnectAttempt = millis(); // Reset the reconnect attempt timer
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Connecting to Wi-Fi...");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Connecting to Wi-Fi...");
|
||||
#endif
|
||||
WiFi.begin(ssid.c_str(), password.c_str(), wifi_channel);
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi already connected.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi already connected.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -143,10 +143,11 @@ static void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) {
|
|||
clear_event(EVENT_WIFI_DISCONNECT);
|
||||
set_event(EVENT_WIFI_CONNECT, 0);
|
||||
connected_once = true;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi connected.");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Wi-Fi connected. RSSI: ");
|
||||
logging.print(-WiFi.RSSI());
|
||||
logging.print(" dBm, IP address: ");
|
||||
logging.println(WiFi.localIP().toString());
|
||||
#endif
|
||||
hasConnectedBefore = true; // Mark as successfully connected at least once
|
||||
reconnectAttempts = 0; // Reset the attempt counter
|
||||
|
@ -159,10 +160,10 @@ static void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) {
|
|||
static void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
//clear disconnects events if we got a IP
|
||||
clear_event(EVENT_WIFI_DISCONNECT);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi Got IP.");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Wi-Fi Got IP. ");
|
||||
logging.print("IP address: ");
|
||||
logging.println(WiFi.localIP().toString());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -170,8 +171,8 @@ static void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
|
|||
static void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
if (connected_once)
|
||||
set_event(EVENT_WIFI_DISCONNECT, 0);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Wi-Fi disconnected.");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi disconnected.");
|
||||
#endif
|
||||
//we dont do anything here, the reconnect will be handled by the monitor
|
||||
//too many events received when the connection is lost
|
||||
|
@ -188,8 +189,8 @@ void init_mDNS() {
|
|||
|
||||
// Initialize mDNS .local resolution
|
||||
if (!MDNS.begin(mdnsHost)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Error setting up MDNS responder!");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Error setting up MDNS responder!");
|
||||
#endif
|
||||
} else {
|
||||
// Advertise via bonjour the service so we can auto discover these battery emulators on the local network.
|
||||
|
@ -200,16 +201,16 @@ void init_mDNS() {
|
|||
|
||||
#ifdef WIFIAP
|
||||
void init_WiFi_AP() {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Creating Access Point: " + String(ssidAP));
|
||||
Serial.println("With password: " + String(passwordAP));
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Creating Access Point: " + String(ssidAP));
|
||||
logging.println("With password: " + String(passwordAP));
|
||||
#endif
|
||||
WiFi.softAP(ssidAP, passwordAP);
|
||||
IPAddress IP = WiFi.softAPIP();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Access Point created.");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(IP);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Access Point created.");
|
||||
logging.print("IP address: ");
|
||||
logging.println(IP);
|
||||
#endif
|
||||
}
|
||||
#endif // WIFIAP
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "devboard/hal/hal.h"
|
||||
#include "devboard/safety/safety.h"
|
||||
#include "devboard/utils/logging.h"
|
||||
#include "devboard/utils/time_meas.h"
|
||||
#include "devboard/utils/types.h"
|
||||
|
||||
|
@ -22,15 +23,15 @@
|
|||
#error You must select a HW to run on!
|
||||
#endif
|
||||
|
||||
#if defined(DUAL_CAN) && defined(CAN_FD)
|
||||
#if defined(CAN_ADDON) && defined(CANFD_ADDON)
|
||||
// Check that user did not try to use dual can and fd-can on same hardware pins
|
||||
#error CAN-FD AND DUAL-CAN CANNOT BE USED SIMULTANEOUSLY
|
||||
#error CAN_ADDON AND CANFD_ADDON CANNOT BE USED SIMULTANEOUSLY
|
||||
#endif
|
||||
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
#if !defined(CAN_FD)
|
||||
#if !defined(CANFD_ADDON)
|
||||
// Check that user did not try to use classic CAN over FD, without FD component
|
||||
#error PLEASE ENABLE CAN_FD TO USE CLASSIC CAN OVER CANFD INTERFACE
|
||||
#error PLEASE ENABLE CANFD_ADDON TO USE CLASSIC CAN OVER CANFD INTERFACE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Me
|
|||
static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
|
||||
static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
|
||||
|
||||
#define VOLTAGE_OFFSET_DV 20
|
||||
|
||||
CAN_frame BYD_250 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
@ -98,12 +100,22 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
|||
}
|
||||
|
||||
//Map values to CAN messages
|
||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
||||
BYD_110.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
|
||||
BYD_110.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
|
||||
//Minvoltage (eg 300.0V = 3000 , 16bits long)
|
||||
BYD_110.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8);
|
||||
BYD_110.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
|
||||
if (datalayer.battery.settings.user_set_voltage_limits_active) { //If user is requesting a specific voltage
|
||||
//Target charge voltage (eg 400.0V = 4000 , 16bits long)
|
||||
BYD_110.data.u8[0] = (datalayer.battery.settings.max_user_set_charge_voltage_dV >> 8);
|
||||
BYD_110.data.u8[1] = (datalayer.battery.settings.max_user_set_charge_voltage_dV & 0x00FF);
|
||||
//Target discharge voltage (eg 300.0V = 3000 , 16bits long)
|
||||
BYD_110.data.u8[2] = (datalayer.battery.settings.max_user_set_discharge_voltage_dV >> 8);
|
||||
BYD_110.data.u8[3] = (datalayer.battery.settings.max_user_set_discharge_voltage_dV & 0x00FF);
|
||||
} else { //Use the voltage based on battery reported design voltage +- offset to avoid triggering events
|
||||
//Target charge voltage (eg 400.0V = 4000 , 16bits long)
|
||||
BYD_110.data.u8[0] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) >> 8);
|
||||
BYD_110.data.u8[1] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) & 0x00FF);
|
||||
//Target discharge voltage (eg 300.0V = 3000 , 16bits long)
|
||||
BYD_110.data.u8[2] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) >> 8);
|
||||
BYD_110.data.u8[3] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) & 0x00FF);
|
||||
}
|
||||
|
||||
//Maximum discharge power allowed (Unit: A+1)
|
||||
BYD_110.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8);
|
||||
BYD_110.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF);
|
||||
|
@ -141,13 +153,13 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
|||
BYD_210.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8);
|
||||
BYD_210.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF);
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
if (inverter_name[0] != 0) {
|
||||
Serial.print("Detected inverter: ");
|
||||
logging.print("Detected inverter: ");
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
Serial.print((char)inverter_name[i]);
|
||||
logging.print((char)inverter_name[i]);
|
||||
}
|
||||
Serial.println();
|
||||
logging.println();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -595,8 +595,8 @@ void send_can_inverter() { // This function loops as fast as possible
|
|||
// Send a subset of messages per iteration to avoid overloading the CAN bus / transmit buffer
|
||||
switch (can_message_cellvolt_index) {
|
||||
case 0:
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Sending large batch");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending large batch");
|
||||
#endif
|
||||
transmit_can(&FOXESS_0C1D, can_config.inverter);
|
||||
transmit_can(&FOXESS_0C21, can_config.inverter);
|
||||
|
@ -655,8 +655,8 @@ void send_can_inverter() { // This function loops as fast as possible
|
|||
transmit_can(&FOXESS_0D49, can_config.inverter);
|
||||
transmit_can(&FOXESS_0D51, can_config.inverter);
|
||||
transmit_can(&FOXESS_0D59, can_config.inverter);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Sending completed");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending completed");
|
||||
#endif
|
||||
send_cellvoltages = false;
|
||||
break;
|
||||
|
@ -679,14 +679,14 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
if (rx_frame.data.u8[0] == 0x03) { //0x1871 [0x03, 0x06, 0x17, 0x05, 0x09, 0x09, 0x28, 0x22]
|
||||
//This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers)
|
||||
//and contains a timestamp in bytes 2-7 i.e. <YY>,<MM>,<DD>,<HH>,<mm>,<ss>
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter sends current time and date");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter sends current time and date");
|
||||
#endif
|
||||
} else if (rx_frame.data.u8[0] == 0x01) {
|
||||
if (rx_frame.data.u8[4] == 0x00) {
|
||||
// Inverter wants to know bms info (every 1s)
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter requests 1s BMS info, we reply");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests 1s BMS info, we reply");
|
||||
#endif
|
||||
transmit_can(&FOXESS_1872, can_config.inverter);
|
||||
transmit_can(&FOXESS_1873, can_config.inverter);
|
||||
|
@ -698,8 +698,8 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
transmit_can(&FOXESS_1879, can_config.inverter);
|
||||
} else if (rx_frame.data.u8[4] == 0x01) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
//Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply)
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter requests individual battery pack status, we reply");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests individual battery pack status, we reply");
|
||||
#endif
|
||||
transmit_can(&FOXESS_0C05, can_config.inverter); //TODO, should we limit this incase NUMBER_OF_PACKS =! 8?
|
||||
transmit_can(&FOXESS_0C06, can_config.inverter);
|
||||
|
@ -711,19 +711,19 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
transmit_can(&FOXESS_0C0C, can_config.inverter);
|
||||
} else if (rx_frame.data.u8[4] == 0x04) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
//Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply)
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter requests cellvoltages and temps, we reply");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests cellvoltages and temps, we reply");
|
||||
#endif
|
||||
send_cellvoltages = true;
|
||||
}
|
||||
} else if (rx_frame.data.u8[0] == 0x02) { //0x1871 [0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
// Ack message
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter acks, no reply needed");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter acks, no reply needed");
|
||||
#endif
|
||||
} else if (rx_frame.data.u8[0] == 0x05) { //0x1871 [0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Inverter wants to know serial numbers, we reply");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter wants to know serial numbers, we reply");
|
||||
#endif
|
||||
for (uint8_t i = 0; i < (NUMBER_OF_PACKS + 1); i++) {
|
||||
FOXESS_1881.data.u8[0] = (uint8_t)i;
|
||||
|
|
|
@ -119,15 +119,15 @@ void float2frameMSB(byte* arr, float value, byte framepointer) {
|
|||
|
||||
void send_kostal(byte* arr, int alen) {
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||
Serial.print("TX: ");
|
||||
logging.print("TX: ");
|
||||
for (int i = 0; i < alen; i++) {
|
||||
if (arr[i] < 0x10) {
|
||||
Serial.print("0");
|
||||
logging.print("0");
|
||||
}
|
||||
Serial.print(arr[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(arr[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("\n");
|
||||
logging.println("\n");
|
||||
#endif
|
||||
Serial2.write(arr, alen);
|
||||
}
|
||||
|
@ -274,12 +274,12 @@ void receive_RS485() // Runs as fast as possible to handle the serial stream
|
|||
if (RS485_RXFRAME[rx_index - 1] == 0x00) {
|
||||
if ((rx_index == 10) && (RS485_RXFRAME[0] == 0x09) && register_content_ok) {
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||
Serial.print("RX: ");
|
||||
logging.print("RX: ");
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
Serial.print(RS485_RXFRAME[i], HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(RS485_RXFRAME[i], HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
#endif
|
||||
rx_index = 0;
|
||||
if (check_kostal_frame_crc()) {
|
||||
|
|
|
@ -60,8 +60,8 @@ void manageSerialLinkTransmitter() {
|
|||
}
|
||||
bool sendError = dataLinkTransmit.checkTransmissionError(true);
|
||||
if (sendError) {
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - ERROR: Serial Data Link - SEND Error");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - ERROR: Serial Data Link - SEND Error");
|
||||
lasterror = true;
|
||||
transmitGoodSince = currentTime;
|
||||
}
|
||||
|
@ -82,17 +82,17 @@ void manageSerialLinkTransmitter() {
|
|||
|
||||
if (lasterror && (ackReceived > 0)) {
|
||||
lasterror = false;
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - RECOVERY: Serial Data Link - Send GOOD");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - RECOVERY: Serial Data Link - Send GOOD");
|
||||
}
|
||||
|
||||
//--- reporting every 60 seconds that transmission is good
|
||||
if (currentTime - transmitGoodSince > INTERVAL_60_S) {
|
||||
transmitGoodSince = currentTime;
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - Transmit Good");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - Transmit Good");
|
||||
// printUsefullData();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
#ifdef DEBUG_LOG
|
||||
void printSendingValues();
|
||||
#endif
|
||||
}
|
||||
|
@ -100,13 +100,13 @@ void manageSerialLinkTransmitter() {
|
|||
//--- report that Errors been ocurring for > 60 seconds
|
||||
if (currentTime - lastGood > INTERVAL_60_S) {
|
||||
lastGood = currentTime;
|
||||
Serial.print(currentTime);
|
||||
Serial.println(" - Transmit Failed : 60 seconds");
|
||||
logging.print(currentTime);
|
||||
logging.println(" - Transmit Failed : 60 seconds");
|
||||
// print the max_ data
|
||||
Serial.println("SerialDataLink : bms_status=4");
|
||||
Serial.println("SerialDataLink : LEDcolor = RED");
|
||||
Serial.println("SerialDataLink : max_target_discharge_power = 0");
|
||||
Serial.println("SerialDataLink : max_target_charge_power = 0");
|
||||
logging.println("SerialDataLink : bms_status=4");
|
||||
logging.println("SerialDataLink : LEDcolor = RED");
|
||||
logging.println("SerialDataLink : max_target_discharge_power = 0");
|
||||
logging.println("SerialDataLink : max_target_charge_power = 0");
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
|
@ -117,8 +117,8 @@ void manageSerialLinkTransmitter() {
|
|||
// lastMessageReceived from CAN bus (Battery)
|
||||
if (currentTime - lastMessageReceived > (5 * 60000) ) // 5 minutes
|
||||
{
|
||||
Serial.print(millis());
|
||||
Serial.println(" - Data Stale : 5 minutes");
|
||||
logging.print(millis());
|
||||
logging.println(" - Data Stale : 5 minutes");
|
||||
// throw error
|
||||
|
||||
// stop transmitting until fresh
|
||||
|
@ -154,42 +154,42 @@ void manageSerialLinkTransmitter() {
|
|||
}
|
||||
|
||||
void printSendingValues() {
|
||||
Serial.println("Values from battery: ");
|
||||
Serial.print("SOC: ");
|
||||
Serial.print(datalayer.battery.status.real_soc);
|
||||
Serial.print(" SOH: ");
|
||||
Serial.print(datalayer.battery.status.soh_pptt);
|
||||
Serial.print(" Voltage: ");
|
||||
Serial.print(datalayer.battery.status.voltage_dV);
|
||||
Serial.print(" Current: ");
|
||||
Serial.print(datalayer.battery.status.current_dA);
|
||||
Serial.print(" Capacity: ");
|
||||
Serial.print(datalayer.battery.info.total_capacity_Wh);
|
||||
Serial.print(" Remain cap: ");
|
||||
Serial.print(datalayer.battery.status.remaining_capacity_Wh);
|
||||
Serial.print(" Max discharge W: ");
|
||||
Serial.print(datalayer.battery.status.max_discharge_power_W);
|
||||
Serial.print(" Max charge W: ");
|
||||
Serial.print(datalayer.battery.status.max_charge_power_W);
|
||||
Serial.print(" BMS status: ");
|
||||
Serial.print(datalayer.battery.status.bms_status);
|
||||
Serial.print(" Power: ");
|
||||
Serial.print(datalayer.battery.status.active_power_W);
|
||||
Serial.print(" Temp min: ");
|
||||
Serial.print(datalayer.battery.status.temperature_min_dC);
|
||||
Serial.print(" Temp max: ");
|
||||
Serial.print(datalayer.battery.status.temperature_max_dC);
|
||||
Serial.print(" Cell max: ");
|
||||
Serial.print(datalayer.battery.status.cell_max_voltage_mV);
|
||||
Serial.print(" Cell min: ");
|
||||
Serial.print(datalayer.battery.status.cell_min_voltage_mV);
|
||||
Serial.print(" LFP : ");
|
||||
Serial.print(datalayer.battery.info.chemistry);
|
||||
Serial.print(" Battery Allows Contactor Closing: ");
|
||||
Serial.print(datalayer.system.status.battery_allows_contactor_closing);
|
||||
Serial.print(" Inverter Allows Contactor Closing: ");
|
||||
Serial.print(datalayer.system.status.inverter_allows_contactor_closing);
|
||||
logging.println("Values from battery: ");
|
||||
logging.print("SOC: ");
|
||||
logging.print(datalayer.battery.status.real_soc);
|
||||
logging.print(" SOH: ");
|
||||
logging.print(datalayer.battery.status.soh_pptt);
|
||||
logging.print(" Voltage: ");
|
||||
logging.print(datalayer.battery.status.voltage_dV);
|
||||
logging.print(" Current: ");
|
||||
logging.print(datalayer.battery.status.current_dA);
|
||||
logging.print(" Capacity: ");
|
||||
logging.print(datalayer.battery.info.total_capacity_Wh);
|
||||
logging.print(" Remain cap: ");
|
||||
logging.print(datalayer.battery.status.remaining_capacity_Wh);
|
||||
logging.print(" Max discharge W: ");
|
||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
||||
logging.print(" Max charge W: ");
|
||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
||||
logging.print(" BMS status: ");
|
||||
logging.print(datalayer.battery.status.bms_status);
|
||||
logging.print(" Power: ");
|
||||
logging.print(datalayer.battery.status.active_power_W);
|
||||
logging.print(" Temp min: ");
|
||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
||||
logging.print(" Temp max: ");
|
||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
||||
logging.print(" Cell max: ");
|
||||
logging.print(datalayer.battery.status.cell_max_voltage_mV);
|
||||
logging.print(" Cell min: ");
|
||||
logging.print(datalayer.battery.status.cell_min_voltage_mV);
|
||||
logging.print(" LFP : ");
|
||||
logging.print(datalayer.battery.info.chemistry);
|
||||
logging.print(" Battery Allows Contactor Closing: ");
|
||||
logging.print(datalayer.system.status.battery_allows_contactor_closing);
|
||||
logging.print(" Inverter Allows Contactor Closing: ");
|
||||
logging.print(datalayer.system.status.inverter_allows_contactor_closing);
|
||||
|
||||
Serial.println("");
|
||||
logging.println("");
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -64,6 +64,31 @@ CAN_frame SOLAX_1879 = {.FD = false,
|
|||
.DLC = 8,
|
||||
.ID = 0x1879,
|
||||
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_187E = {.FD = false, //Needed for Ultra
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
.ID = 0x187E,
|
||||
.data = {0x0, 0x2D, 0x0, 0x0, 0x0, 0x5F, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_187D = {.FD = false, //Needed for Ultra
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
.ID = 0x187D,
|
||||
.data = {0x8B, 0x01, 0x0, 0x0, 0x8B, 0x1, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_187C = {.FD = false, //Needed for Ultra
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
.ID = 0x187C,
|
||||
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_187B = {.FD = false, //Needed for Ultra
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
.ID = 0x187B,
|
||||
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_187A = {.FD = false, //Needed for Ultra
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
.ID = 0x187A,
|
||||
.data = {0x01, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
CAN_frame SOLAX_1881 = {.FD = false,
|
||||
.ext_ID = true,
|
||||
.DLC = 8,
|
||||
|
@ -182,8 +207,8 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
LastFrameTime = millis();
|
||||
switch (STATE) {
|
||||
case (BATTERY_ANNOUNCE):
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Solax Battery State: Announce");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Solax Battery State: Announce");
|
||||
#endif
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false;
|
||||
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
||||
|
@ -214,8 +239,8 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
transmit_can(&SOLAX_1878, can_config.inverter);
|
||||
transmit_can(&SOLAX_1801, can_config.inverter); // Announce that the battery will be connected
|
||||
STATE = CONTACTOR_CLOSED; // Jump to Contactor Closed State
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Solax Battery State: Contactor Closed");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Solax Battery State: Contactor Closed");
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -242,13 +267,13 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
|||
if (rx_frame.ID == 0x1871 && rx_frame.data.u64 == __builtin_bswap64(0x0500010000000000)) {
|
||||
transmit_can(&SOLAX_1881, can_config.inverter);
|
||||
transmit_can(&SOLAX_1882, can_config.inverter);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("1871 05-frame received from inverter");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("1871 05-frame received from inverter");
|
||||
#endif
|
||||
}
|
||||
if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x03)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("1871 03-frame received from inverter");
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("1871 03-frame received from inverter");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -256,5 +281,12 @@ void setup_inverter(void) { // Performs one time setup at startup
|
|||
strncpy(datalayer.system.info.inverter_protocol, "SolaX Triple Power LFP over CAN bus", 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
|
||||
// Sending these messages once towards the inverter makes SOC% work on the Ultra variant
|
||||
transmit_can(&SOLAX_187E, can_config.inverter);
|
||||
transmit_can(&SOLAX_187D, can_config.inverter);
|
||||
transmit_can(&SOLAX_187C, can_config.inverter);
|
||||
transmit_can(&SOLAX_187B, can_config.inverter);
|
||||
transmit_can(&SOLAX_187A, can_config.inverter);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,7 @@ _____ _ _ ___ _____ _
|
|||
#include "Update.h"
|
||||
#include "StreamString.h"
|
||||
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
||||
#include "../../me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../me-no-dev-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||
#define ELEGANTOTA_WEBSERVER AsyncWebServer
|
||||
#else
|
||||
|
|
129
Software/src/lib/mathieucarbou-AsyncTCP/CODE_OF_CONDUCT.md
Normal file
129
Software/src/lib/mathieucarbou-AsyncTCP/CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,129 @@
|
|||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
https://sidweb.nl/cms3/en/contact.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
62
Software/src/lib/mathieucarbou-AsyncTCP/README.md
Normal file
62
Software/src/lib/mathieucarbou-AsyncTCP/README.md
Normal file
|
@ -0,0 +1,62 @@
|
|||
# AsyncTCP
|
||||
|
||||
[](https://opensource.org/license/lgpl-3-0/)
|
||||
[](https://github.com/mathieucarbou/AsyncTCP/actions/workflows/ci.yml)
|
||||
[](https://registry.platformio.org/libraries/mathieucarbou/AsyncTCP)
|
||||
|
||||
A fork of the [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) library by [@me-no-dev](https://github.com/me-no-dev).
|
||||
|
||||
### Async TCP Library for ESP32 Arduino
|
||||
|
||||
This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs.
|
||||
|
||||
This library is the base for [ESPAsyncWebServer](https://github.com/mathieucarbou/ESPAsyncWebServer)
|
||||
|
||||
## AsyncClient and AsyncServer
|
||||
|
||||
The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use.
|
||||
|
||||
## Changes in this fork
|
||||
|
||||
- Based on [ESPHome fork](https://github.com/esphome/AsyncTCP)
|
||||
|
||||
- `library.properties` for Arduino IDE users
|
||||
- Add `CONFIG_ASYNC_TCP_MAX_ACK_TIME`
|
||||
- Add `CONFIG_ASYNC_TCP_PRIORITY`
|
||||
- Add `CONFIG_ASYNC_TCP_QUEUE_SIZE`
|
||||
- Add `setKeepAlive()`
|
||||
- Arduino 3 / ESP-IDF 5 compatibility
|
||||
- Better CI
|
||||
- Better example
|
||||
- Customizable macros
|
||||
- Fix for "Required to lock TCPIP core functionality". Ref: https://github.com/mathieucarbou/AsyncTCP/issues/27 and https://github.com/espressif/arduino-esp32/issues/10526
|
||||
- Fix for "ack timeout 4" client disconnects.
|
||||
- Fix from https://github.com/me-no-dev/AsyncTCP/pull/173 (partially applied)
|
||||
- Fix from https://github.com/me-no-dev/AsyncTCP/pull/184
|
||||
- IPv6
|
||||
- LIBRETINY support
|
||||
- LibreTuya
|
||||
- Reduce logging of non critical messages
|
||||
- Use IPADDR6_INIT() macro to set connecting IPv6 address
|
||||
- xTaskCreateUniversal function
|
||||
|
||||
## Coordinates
|
||||
|
||||
```
|
||||
mathieucarbou/AsyncTCP @ ^3.3.1
|
||||
```
|
||||
|
||||
## Important recommendations
|
||||
|
||||
Most of the crashes are caused by improper configuration of the library for the project.
|
||||
Here are some recommendations to avoid them.
|
||||
|
||||
I personally use the following configuration in my projects:
|
||||
|
||||
```c++
|
||||
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 // (keep default)
|
||||
-D CONFIG_ASYNC_TCP_PRIORITY=10 // (keep default)
|
||||
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 // (keep default)
|
||||
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as the app (default is core 0)
|
||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K)
|
||||
```
|
38
Software/src/lib/mathieucarbou-AsyncTCP/library.json
Normal file
38
Software/src/lib/mathieucarbou-AsyncTCP/library.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "AsyncTCP",
|
||||
"version": "3.3.1",
|
||||
"description": "Asynchronous TCP Library for ESP32",
|
||||
"keywords": "async,tcp",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mathieucarbou/AsyncTCP.git"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hristo Gochkov"
|
||||
},
|
||||
{
|
||||
"name": "Mathieu Carbou",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": [
|
||||
"espressif32",
|
||||
"libretiny"
|
||||
],
|
||||
"build": {
|
||||
"libCompatMode": 2
|
||||
},
|
||||
"export": {
|
||||
"include": [
|
||||
"examples",
|
||||
"src",
|
||||
"library.json",
|
||||
"library.properties",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
}
|
10
Software/src/lib/mathieucarbou-AsyncTCP/library.properties
Normal file
10
Software/src/lib/mathieucarbou-AsyncTCP/library.properties
Normal file
|
@ -0,0 +1,10 @@
|
|||
name=Async TCP
|
||||
includes=AsyncTCP.h
|
||||
version=3.3.1
|
||||
author=Me-No-Dev
|
||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
||||
sentence=Async TCP Library for ESP32
|
||||
paragraph=Async TCP Library for ESP32
|
||||
category=Other
|
||||
url=https://github.com/mathieucarbou/AsyncTCP.git
|
||||
architectures=*
|
43
Software/src/lib/mathieucarbou-AsyncTCP/platformio.ini
Normal file
43
Software/src/lib/mathieucarbou-AsyncTCP/platformio.ini
Normal file
|
@ -0,0 +1,43 @@
|
|||
[platformio]
|
||||
default_envs = arduino-2, arduino-3, arduino-310
|
||||
lib_dir = .
|
||||
src_dir = examples/Client
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-Wall -Wextra
|
||||
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000
|
||||
-D CONFIG_ASYNC_TCP_PRIORITY=10
|
||||
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64
|
||||
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
|
||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
|
||||
-D CONFIG_ARDUHAL_LOG_COLORS
|
||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
upload_protocol = esptool
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder, log2file
|
||||
board = esp32dev
|
||||
|
||||
[env:arduino-2]
|
||||
platform = espressif32@6.9.0
|
||||
|
||||
[env:arduino-3]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
|
||||
[env:arduino-310]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
||||
|
||||
; CI
|
||||
|
||||
[env:ci-arduino-2]
|
||||
platform = espressif32@6.9.0
|
||||
board = ${sysenv.PIO_BOARD}
|
||||
|
||||
[env:ci-arduino-3]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
board = ${sysenv.PIO_BOARD}
|
||||
|
||||
[env:ci-arduino-310]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
||||
board = ${sysenv.PIO_BOARD}
|
1661
Software/src/lib/mathieucarbou-AsyncTCP/src/AsyncTCP.cpp
Normal file
1661
Software/src/lib/mathieucarbou-AsyncTCP/src/AsyncTCP.cpp
Normal file
File diff suppressed because it is too large
Load diff
347
Software/src/lib/mathieucarbou-AsyncTCP/src/AsyncTCP.h
Normal file
347
Software/src/lib/mathieucarbou-AsyncTCP/src/AsyncTCP.h
Normal file
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
Asynchronous TCP library for Espressif MCUs
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ASYNCTCP_H_
|
||||
#define ASYNCTCP_H_
|
||||
|
||||
#define ASYNCTCP_VERSION "3.3.1"
|
||||
#define ASYNCTCP_VERSION_MAJOR 3
|
||||
#define ASYNCTCP_VERSION_MINOR 3
|
||||
#define ASYNCTCP_VERSION_REVISION 1
|
||||
#define ASYNCTCP_FORK_mathieucarbou
|
||||
|
||||
#include "../../../devboard/hal/hal.h"
|
||||
#include "../../../system_settings.h"
|
||||
|
||||
#include "IPAddress.h"
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
#include "IPv6Address.h"
|
||||
#endif
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include <functional>
|
||||
|
||||
#ifndef LIBRETINY
|
||||
#include "sdkconfig.h"
|
||||
extern "C" {
|
||||
#include "freertos/semphr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
}
|
||||
#else
|
||||
extern "C" {
|
||||
#include <lwip/pbuf.h>
|
||||
#include <semphr.h>
|
||||
}
|
||||
#define CONFIG_ASYNC_TCP_RUNNING_CORE WIFI_CORE
|
||||
#endif
|
||||
|
||||
// If core is not defined, then we are running in Arduino or PIO
|
||||
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
|
||||
#define CONFIG_ASYNC_TCP_RUNNING_CORE WIFI_CORE
|
||||
#endif
|
||||
|
||||
// guard AsyncTCP task with watchdog
|
||||
#ifndef CONFIG_ASYNC_TCP_USE_WDT
|
||||
#define CONFIG_ASYNC_TCP_USE_WDT 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ASYNC_TCP_STACK_SIZE
|
||||
#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ASYNC_TCP_PRIORITY
|
||||
#define CONFIG_ASYNC_TCP_PRIORITY 10
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE
|
||||
#define CONFIG_ASYNC_TCP_QUEUE_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME
|
||||
#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000
|
||||
#endif
|
||||
|
||||
class AsyncClient;
|
||||
|
||||
#define ASYNC_WRITE_FLAG_COPY 0x01 // will allocate new buffer to hold the data while sending (else will hold reference to the data given)
|
||||
#define ASYNC_WRITE_FLAG_MORE 0x02 // will not send PSH flag, meaning that there should be more data to be sent before the application should react.
|
||||
|
||||
typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, void* data, size_t len)> AcDataHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, struct pbuf* pb)> AcPacketHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
|
||||
|
||||
struct tcp_pcb;
|
||||
struct ip_addr;
|
||||
|
||||
class AsyncClient {
|
||||
public:
|
||||
AsyncClient(tcp_pcb* pcb = 0);
|
||||
~AsyncClient();
|
||||
|
||||
AsyncClient& operator=(const AsyncClient& other);
|
||||
AsyncClient& operator+=(const AsyncClient& other);
|
||||
|
||||
bool operator==(const AsyncClient& other);
|
||||
|
||||
bool operator!=(const AsyncClient& other) {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool connect(const IPAddress& ip, uint16_t port);
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
bool connect(const IPv6Address& ip, uint16_t port);
|
||||
#endif
|
||||
bool connect(const char* host, uint16_t port);
|
||||
/**
|
||||
* @brief close connection
|
||||
*
|
||||
* @param now - ignored
|
||||
*/
|
||||
void close(bool now = false);
|
||||
// same as close()
|
||||
void stop() { close(false); };
|
||||
int8_t abort();
|
||||
bool free();
|
||||
|
||||
// ack is not pending
|
||||
bool canSend();
|
||||
// TCP buffer space available
|
||||
size_t space();
|
||||
|
||||
/**
|
||||
* @brief add data to be send (but do not send yet)
|
||||
* @note add() would call lwip's tcp_write()
|
||||
By default apiflags=ASYNC_WRITE_FLAG_COPY
|
||||
You could try to use apiflags with this flag unset to pass data by reference and avoid copy to socket buffer,
|
||||
but looks like it does not work for Arduino's lwip in ESP32/IDF at least
|
||||
it is enforced in https://github.com/espressif/esp-lwip/blob/0606eed9d8b98a797514fdf6eabb4daf1c8c8cd9/src/core/tcp_out.c#L422C5-L422C30
|
||||
if LWIP_NETIF_TX_SINGLE_PBUF is set, and it is set indeed in IDF
|
||||
https://github.com/espressif/esp-idf/blob/a0f798cfc4bbd624aab52b2c194d219e242d80c1/components/lwip/port/include/lwipopts.h#L744
|
||||
*
|
||||
* @param data
|
||||
* @param size
|
||||
* @param apiflags
|
||||
* @return size_t amount of data that has been copied
|
||||
*/
|
||||
size_t add(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY);
|
||||
|
||||
/**
|
||||
* @brief send data previously add()'ed
|
||||
*
|
||||
* @return true on success
|
||||
* @return false on error
|
||||
*/
|
||||
bool send();
|
||||
|
||||
/**
|
||||
* @brief add and enqueue data for sending
|
||||
* @note it is same as add() + send()
|
||||
* @note only make sense when canSend() == true
|
||||
*
|
||||
* @param data
|
||||
* @param size
|
||||
* @param apiflags
|
||||
* @return size_t
|
||||
*/
|
||||
size_t write(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY);
|
||||
|
||||
/**
|
||||
* @brief add and enque data for sending
|
||||
* @note treats data as null-terminated string
|
||||
*
|
||||
* @param data
|
||||
* @return size_t
|
||||
*/
|
||||
size_t write(const char* data) { return data == NULL ? 0 : write(data, strlen(data)); };
|
||||
|
||||
uint8_t state();
|
||||
bool connecting();
|
||||
bool connected();
|
||||
bool disconnecting();
|
||||
bool disconnected();
|
||||
|
||||
// disconnected or disconnecting
|
||||
bool freeable();
|
||||
|
||||
uint16_t getMss();
|
||||
|
||||
uint32_t getRxTimeout();
|
||||
// no RX data timeout for the connection in seconds
|
||||
void setRxTimeout(uint32_t timeout);
|
||||
|
||||
uint32_t getAckTimeout();
|
||||
// no ACK timeout for the last sent packet in milliseconds
|
||||
void setAckTimeout(uint32_t timeout);
|
||||
|
||||
void setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
|
||||
void setKeepAlive(uint32_t ms, uint8_t cnt);
|
||||
|
||||
uint32_t getRemoteAddress();
|
||||
uint16_t getRemotePort();
|
||||
uint32_t getLocalAddress();
|
||||
uint16_t getLocalPort();
|
||||
#if LWIP_IPV6
|
||||
ip6_addr_t getRemoteAddress6();
|
||||
ip6_addr_t getLocalAddress6();
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
IPv6Address remoteIP6();
|
||||
IPv6Address localIP6();
|
||||
#else
|
||||
IPAddress remoteIP6();
|
||||
IPAddress localIP6();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// compatibility
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
IPAddress localIP();
|
||||
uint16_t localPort();
|
||||
|
||||
// set callback - on successful connect
|
||||
void onConnect(AcConnectHandler cb, void* arg = 0);
|
||||
// set callback - disconnected
|
||||
void onDisconnect(AcConnectHandler cb, void* arg = 0);
|
||||
// set callback - ack received
|
||||
void onAck(AcAckHandler cb, void* arg = 0);
|
||||
// set callback - unsuccessful connect or error
|
||||
void onError(AcErrorHandler cb, void* arg = 0);
|
||||
// set callback - data received (called if onPacket is not used)
|
||||
void onData(AcDataHandler cb, void* arg = 0);
|
||||
// set callback - data received
|
||||
void onPacket(AcPacketHandler cb, void* arg = 0);
|
||||
// set callback - ack timeout
|
||||
void onTimeout(AcTimeoutHandler cb, void* arg = 0);
|
||||
// set callback - every 125ms when connected
|
||||
void onPoll(AcConnectHandler cb, void* arg = 0);
|
||||
|
||||
// ack pbuf from onPacket
|
||||
void ackPacket(struct pbuf* pb);
|
||||
// ack data that you have not acked using the method below
|
||||
size_t ack(size_t len);
|
||||
// will not ack the current packet. Call from onData
|
||||
void ackLater() { _ack_pcb = false; }
|
||||
|
||||
static const char* errorToString(int8_t error);
|
||||
const char* stateToString();
|
||||
|
||||
// internal callbacks - Do NOT call any of the functions below in user code!
|
||||
static int8_t _s_poll(void* arg, struct tcp_pcb* tpcb);
|
||||
static int8_t _s_recv(void* arg, struct tcp_pcb* tpcb, struct pbuf* pb, int8_t err);
|
||||
static int8_t _s_fin(void* arg, struct tcp_pcb* tpcb, int8_t err);
|
||||
static int8_t _s_lwip_fin(void* arg, struct tcp_pcb* tpcb, int8_t err);
|
||||
static void _s_error(void* arg, int8_t err);
|
||||
static int8_t _s_sent(void* arg, struct tcp_pcb* tpcb, uint16_t len);
|
||||
static int8_t _s_connected(void* arg, struct tcp_pcb* tpcb, int8_t err);
|
||||
static void _s_dns_found(const char* name, struct ip_addr* ipaddr, void* arg);
|
||||
|
||||
int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
|
||||
tcp_pcb* pcb() { return _pcb; }
|
||||
|
||||
protected:
|
||||
bool _connect(ip_addr_t addr, uint16_t port);
|
||||
|
||||
tcp_pcb* _pcb;
|
||||
int8_t _closed_slot;
|
||||
|
||||
AcConnectHandler _connect_cb;
|
||||
void* _connect_cb_arg;
|
||||
AcConnectHandler _discard_cb;
|
||||
void* _discard_cb_arg;
|
||||
AcAckHandler _sent_cb;
|
||||
void* _sent_cb_arg;
|
||||
AcErrorHandler _error_cb;
|
||||
void* _error_cb_arg;
|
||||
AcDataHandler _recv_cb;
|
||||
void* _recv_cb_arg;
|
||||
AcPacketHandler _pb_cb;
|
||||
void* _pb_cb_arg;
|
||||
AcTimeoutHandler _timeout_cb;
|
||||
void* _timeout_cb_arg;
|
||||
AcConnectHandler _poll_cb;
|
||||
void* _poll_cb_arg;
|
||||
|
||||
bool _ack_pcb;
|
||||
uint32_t _tx_last_packet;
|
||||
uint32_t _rx_ack_len;
|
||||
uint32_t _rx_last_packet;
|
||||
uint32_t _rx_timeout;
|
||||
uint32_t _rx_last_ack;
|
||||
uint32_t _ack_timeout;
|
||||
uint16_t _connect_port;
|
||||
|
||||
int8_t _close();
|
||||
void _free_closed_slot();
|
||||
bool _allocate_closed_slot();
|
||||
int8_t _connected(tcp_pcb* pcb, int8_t err);
|
||||
void _error(int8_t err);
|
||||
int8_t _poll(tcp_pcb* pcb);
|
||||
int8_t _sent(tcp_pcb* pcb, uint16_t len);
|
||||
int8_t _fin(tcp_pcb* pcb, int8_t err);
|
||||
int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
|
||||
void _dns_found(struct ip_addr* ipaddr);
|
||||
|
||||
public:
|
||||
AsyncClient* prev;
|
||||
AsyncClient* next;
|
||||
};
|
||||
|
||||
class AsyncServer {
|
||||
public:
|
||||
AsyncServer(IPAddress addr, uint16_t port);
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
AsyncServer(IPv6Address addr, uint16_t port);
|
||||
#endif
|
||||
AsyncServer(uint16_t port);
|
||||
~AsyncServer();
|
||||
void onClient(AcConnectHandler cb, void* arg);
|
||||
void begin();
|
||||
void end();
|
||||
void setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
uint8_t status();
|
||||
|
||||
// Do not use any of the functions below!
|
||||
static int8_t _s_accept(void* arg, tcp_pcb* newpcb, int8_t err);
|
||||
static int8_t _s_accepted(void* arg, AsyncClient* client);
|
||||
|
||||
protected:
|
||||
uint16_t _port;
|
||||
bool _bind4 = false;
|
||||
bool _bind6 = false;
|
||||
IPAddress _addr;
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
IPv6Address _addr6;
|
||||
#endif
|
||||
bool _noDelay;
|
||||
tcp_pcb* _pcb;
|
||||
AcConnectHandler _connect_cb;
|
||||
void* _connect_cb_arg;
|
||||
|
||||
int8_t _accept(tcp_pcb* newpcb, int8_t err);
|
||||
int8_t _accepted(AsyncClient* client);
|
||||
};
|
||||
|
||||
#endif /* ASYNCTCP_H_ */
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
.DS_Store
|
|
@ -1,34 +0,0 @@
|
|||
sudo: false
|
||||
language: python
|
||||
os:
|
||||
- linux
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
- name: "Arduino Build"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh
|
||||
|
||||
- name: "PlatformIO Build"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 1 1
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/60e65d0c78ea0a920347
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
|
@ -1,30 +0,0 @@
|
|||
menu "AsyncTCP Configuration"
|
||||
|
||||
choice ASYNC_TCP_RUNNING_CORE
|
||||
bool "Core on which AsyncTCP's thread is running"
|
||||
default ASYNC_TCP_RUN_CORE1
|
||||
help
|
||||
Select on which core AsyncTCP is running
|
||||
|
||||
config ASYNC_TCP_RUN_CORE0
|
||||
bool "CORE 0"
|
||||
config ASYNC_TCP_RUN_CORE1
|
||||
bool "CORE 1"
|
||||
config ASYNC_TCP_RUN_NO_AFFINITY
|
||||
bool "BOTH"
|
||||
|
||||
endchoice
|
||||
|
||||
config ASYNC_TCP_RUNNING_CORE
|
||||
int
|
||||
default 0 if ASYNC_TCP_RUN_CORE0
|
||||
default 1 if ASYNC_TCP_RUN_CORE1
|
||||
default -1 if ASYNC_TCP_RUN_NO_AFFINITY
|
||||
|
||||
config ASYNC_TCP_USE_WDT
|
||||
bool "Enable WDT for the AsyncTCP task"
|
||||
default "y"
|
||||
help
|
||||
Enable WDT for the AsyncTCP task, so it will trigger if a handler is locking the thread.
|
||||
|
||||
endmenu
|
|
@ -1,15 +0,0 @@
|
|||
This is commit ca8ac5f from https://github.com/me-no-dev/AsyncTCP
|
||||
|
||||
# AsyncTCP
|
||||
[](https://travis-ci.org/me-no-dev/AsyncTCP)  [](https://www.codacy.com/manual/me-no-dev/AsyncTCP?utm_source=github.com&utm_medium=referral&utm_content=me-no-dev/AsyncTCP&utm_campaign=Badge_Grade)
|
||||
|
||||
### Async TCP Library for ESP32 Arduino
|
||||
|
||||
[](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs.
|
||||
|
||||
This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
|
||||
|
||||
## AsyncClient and AsyncServer
|
||||
The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use.
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"name":"AsyncTCP",
|
||||
"description":"Asynchronous TCP Library for ESP32",
|
||||
"keywords":"async,tcp",
|
||||
"authors":
|
||||
{
|
||||
"name": "Hristo Gochkov",
|
||||
"maintainer": true
|
||||
},
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/me-no-dev/AsyncTCP.git"
|
||||
},
|
||||
"version": "1.1.1",
|
||||
"license": "LGPL-3.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif32",
|
||||
"build": {
|
||||
"libCompatMode": 2
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
name=AsyncTCP
|
||||
version=1.1.1
|
||||
author=Me-No-Dev
|
||||
maintainer=Me-No-Dev
|
||||
sentence=Async TCP Library for ESP32
|
||||
paragraph=Async TCP Library for ESP32
|
||||
category=Other
|
||||
url=https://github.com/me-no-dev/AsyncTCP
|
||||
architectures=*
|
File diff suppressed because it is too large
Load diff
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
Asynchronous TCP library for Espressif MCUs
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ASYNCTCP_H_
|
||||
#define ASYNCTCP_H_
|
||||
|
||||
#include "IPAddress.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <functional>
|
||||
extern "C" {
|
||||
#include "freertos/semphr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
}
|
||||
|
||||
#include "../../../system_settings.h"
|
||||
#include "../../../devboard/hal/hal.h"
|
||||
|
||||
//If core is not defined, then we are running in Arduino or PIO
|
||||
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
|
||||
#define CONFIG_ASYNC_TCP_RUNNING_CORE WIFI_CORE //any available core
|
||||
#define CONFIG_ASYNC_TCP_USE_WDT 0 //if enabled, adds between 33us and 200us per event
|
||||
#endif
|
||||
|
||||
class AsyncClient;
|
||||
|
||||
#define ASYNC_MAX_ACK_TIME 5000
|
||||
#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
|
||||
#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.
|
||||
|
||||
typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
|
||||
|
||||
struct tcp_pcb;
|
||||
struct ip_addr;
|
||||
|
||||
class AsyncClient {
|
||||
public:
|
||||
AsyncClient(tcp_pcb* pcb = 0);
|
||||
~AsyncClient();
|
||||
|
||||
AsyncClient & operator=(const AsyncClient &other);
|
||||
AsyncClient & operator+=(const AsyncClient &other);
|
||||
|
||||
bool operator==(const AsyncClient &other);
|
||||
|
||||
bool operator!=(const AsyncClient &other) {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool connect(IPAddress ip, uint16_t port);
|
||||
bool connect(const char* host, uint16_t port);
|
||||
void close(bool now = false);
|
||||
void stop();
|
||||
int8_t abort();
|
||||
bool free();
|
||||
|
||||
bool canSend();//ack is not pending
|
||||
size_t space();//space available in the TCP window
|
||||
size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
|
||||
bool send();//send all data added with the method above
|
||||
|
||||
//write equals add()+send()
|
||||
size_t write(const char* data);
|
||||
size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true
|
||||
|
||||
uint8_t state();
|
||||
bool connecting();
|
||||
bool connected();
|
||||
bool disconnecting();
|
||||
bool disconnected();
|
||||
bool freeable();//disconnected or disconnecting
|
||||
|
||||
uint16_t getMss();
|
||||
|
||||
uint32_t getRxTimeout();
|
||||
void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds
|
||||
|
||||
uint32_t getAckTimeout();
|
||||
void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds
|
||||
|
||||
void setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
|
||||
uint32_t getRemoteAddress();
|
||||
uint16_t getRemotePort();
|
||||
uint32_t getLocalAddress();
|
||||
uint16_t getLocalPort();
|
||||
|
||||
//compatibility
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
IPAddress localIP();
|
||||
uint16_t localPort();
|
||||
|
||||
void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect
|
||||
void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
|
||||
void onAck(AcAckHandler cb, void* arg = 0); //ack received
|
||||
void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error
|
||||
void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used)
|
||||
void onPacket(AcPacketHandler cb, void* arg = 0); //data received
|
||||
void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout
|
||||
void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected
|
||||
|
||||
void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
|
||||
size_t ack(size_t len); //ack data that you have not acked using the method below
|
||||
void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
|
||||
|
||||
const char * errorToString(int8_t error);
|
||||
const char * stateToString();
|
||||
|
||||
//Do not use any of the functions below!
|
||||
static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb);
|
||||
static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err);
|
||||
static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
|
||||
static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
|
||||
static void _s_error(void *arg, int8_t err);
|
||||
static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
|
||||
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
|
||||
static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
|
||||
|
||||
int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
|
||||
tcp_pcb * pcb(){ return _pcb; }
|
||||
|
||||
protected:
|
||||
tcp_pcb* _pcb;
|
||||
int8_t _closed_slot;
|
||||
|
||||
AcConnectHandler _connect_cb;
|
||||
void* _connect_cb_arg;
|
||||
AcConnectHandler _discard_cb;
|
||||
void* _discard_cb_arg;
|
||||
AcAckHandler _sent_cb;
|
||||
void* _sent_cb_arg;
|
||||
AcErrorHandler _error_cb;
|
||||
void* _error_cb_arg;
|
||||
AcDataHandler _recv_cb;
|
||||
void* _recv_cb_arg;
|
||||
AcPacketHandler _pb_cb;
|
||||
void* _pb_cb_arg;
|
||||
AcTimeoutHandler _timeout_cb;
|
||||
void* _timeout_cb_arg;
|
||||
AcConnectHandler _poll_cb;
|
||||
void* _poll_cb_arg;
|
||||
|
||||
bool _pcb_busy;
|
||||
uint32_t _pcb_sent_at;
|
||||
bool _ack_pcb;
|
||||
uint32_t _rx_ack_len;
|
||||
uint32_t _rx_last_packet;
|
||||
uint32_t _rx_since_timeout;
|
||||
uint32_t _ack_timeout;
|
||||
uint16_t _connect_port;
|
||||
|
||||
int8_t _close();
|
||||
void _free_closed_slot();
|
||||
void _allocate_closed_slot();
|
||||
int8_t _connected(void* pcb, int8_t err);
|
||||
void _error(int8_t err);
|
||||
int8_t _poll(tcp_pcb* pcb);
|
||||
int8_t _sent(tcp_pcb* pcb, uint16_t len);
|
||||
int8_t _fin(tcp_pcb* pcb, int8_t err);
|
||||
int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
|
||||
void _dns_found(struct ip_addr *ipaddr);
|
||||
|
||||
public:
|
||||
AsyncClient* prev;
|
||||
AsyncClient* next;
|
||||
};
|
||||
|
||||
class AsyncServer {
|
||||
public:
|
||||
AsyncServer(IPAddress addr, uint16_t port);
|
||||
AsyncServer(uint16_t port);
|
||||
~AsyncServer();
|
||||
void onClient(AcConnectHandler cb, void* arg);
|
||||
void begin();
|
||||
void end();
|
||||
void setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
uint8_t status();
|
||||
|
||||
//Do not use any of the functions below!
|
||||
static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err);
|
||||
static int8_t _s_accepted(void *arg, AsyncClient* client);
|
||||
|
||||
protected:
|
||||
uint16_t _port;
|
||||
IPAddress _addr;
|
||||
bool _noDelay;
|
||||
tcp_pcb* _pcb;
|
||||
AcConnectHandler _connect_cb;
|
||||
void* _connect_cb_arg;
|
||||
|
||||
int8_t _accept(tcp_pcb* newpcb, int8_t err);
|
||||
int8_t _accepted(AsyncClient* client);
|
||||
};
|
||||
|
||||
|
||||
#endif /* ASYNCTCP_H_ */
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include "../../me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include "../../me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#define WS_MAX_QUEUED_MESSAGES 32
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include "../../me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "../../me-no-dev-AsyncTCP/src/AsyncTCP.h"
|
||||
#include "../../mathieucarbou-AsyncTCP/src/AsyncTCP.h"
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue