├── .clang-format ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── compile-all-batteries.yml │ ├── compile-all-combinations.yml │ ├── compile-all-double-batteries.yml │ ├── compile-all-hardware.yml │ ├── compile-all-inverters.yml │ ├── run-pre-commit.yml │ └── unit-tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Software ├── Software.ino ├── USER_SECRETS.TEMPLATE.h ├── USER_SETTINGS.cpp ├── USER_SETTINGS.h └── src │ ├── battery │ ├── BATTERIES.cpp │ ├── BATTERIES.h │ ├── BMW-I3-BATTERY.cpp │ ├── BMW-I3-BATTERY.h │ ├── BMW-I3-HTML.h │ ├── BMW-IX-BATTERY.cpp │ ├── BMW-IX-BATTERY.h │ ├── BMW-IX-HTML.h │ ├── BMW-PHEV-BATTERY.cpp │ ├── BMW-PHEV-BATTERY.h │ ├── BMW-PHEV-HTML.h │ ├── BMW-SBOX.cpp │ ├── BMW-SBOX.h │ ├── BOLT-AMPERA-BATTERY.cpp │ ├── BOLT-AMPERA-BATTERY.h │ ├── BOLT-AMPERA-HTML.h │ ├── BYD-ATTO-3-BATTERY.cpp │ ├── BYD-ATTO-3-BATTERY.h │ ├── BYD-ATTO-3-HTML.h │ ├── Battery.h │ ├── CELLPOWER-BMS.cpp │ ├── CELLPOWER-BMS.h │ ├── CELLPOWER-HTML.h │ ├── CHADEMO-BATTERY.cpp │ ├── CHADEMO-BATTERY.h │ ├── CHADEMO-SHUNTS.cpp │ ├── CHADEMO-SHUNTS.h │ ├── CMFA-EV-BATTERY.cpp │ ├── CMFA-EV-BATTERY.h │ ├── CMFA-EV-HTML.h │ ├── CanBattery.h │ ├── DALY-BMS.cpp │ ├── DALY-BMS.h │ ├── ECMP-BATTERY.cpp │ ├── ECMP-BATTERY.h │ ├── ECMP-HTML.h │ ├── FOXESS-BATTERY.cpp │ ├── FOXESS-BATTERY.h │ ├── GEELY-GEOMETRY-C-BATTERY.cpp │ ├── GEELY-GEOMETRY-C-BATTERY.h │ ├── GEELY-GEOMETRY-C-HTML.h │ ├── IMIEV-CZERO-ION-BATTERY.cpp │ ├── IMIEV-CZERO-ION-BATTERY.h │ ├── JAGUAR-IPACE-BATTERY.cpp │ ├── JAGUAR-IPACE-BATTERY.h │ ├── KIA-E-GMP-BATTERY.cpp │ ├── KIA-E-GMP-BATTERY.h │ ├── KIA-HYUNDAI-64-BATTERY.cpp │ ├── KIA-HYUNDAI-64-BATTERY.h │ ├── KIA-HYUNDAI-64-HTML.h │ ├── KIA-HYUNDAI-HYBRID-BATTERY.cpp │ ├── KIA-HYUNDAI-HYBRID-BATTERY.h │ ├── MEB-BATTERY.cpp │ ├── MEB-BATTERY.h │ ├── MEB-HTML.h │ ├── MG-5-BATTERY.cpp │ ├── MG-5-BATTERY.h │ ├── NISSAN-LEAF-BATTERY.cpp │ ├── NISSAN-LEAF-BATTERY.h │ ├── NISSAN-LEAF-HTML.h │ ├── ORION-BMS.cpp │ ├── ORION-BMS.h │ ├── PYLON-BATTERY.cpp │ ├── PYLON-BATTERY.h │ ├── RANGE-ROVER-PHEV-BATTERY.cpp │ ├── RANGE-ROVER-PHEV-BATTERY.h │ ├── RENAULT-KANGOO-BATTERY.cpp │ ├── RENAULT-KANGOO-BATTERY.h │ ├── RENAULT-TWIZY.cpp │ ├── RENAULT-TWIZY.h │ ├── RENAULT-ZOE-GEN1-BATTERY.cpp │ ├── RENAULT-ZOE-GEN1-BATTERY.h │ ├── RENAULT-ZOE-GEN1-HTML.h │ ├── RENAULT-ZOE-GEN2-BATTERY.cpp │ ├── RENAULT-ZOE-GEN2-BATTERY.h │ ├── RENAULT-ZOE-GEN2-HTML.h │ ├── RJXZS-BMS.cpp │ ├── RJXZS-BMS.h │ ├── RS485Battery.h │ ├── SANTA-FE-PHEV-BATTERY.cpp │ ├── SANTA-FE-PHEV-BATTERY.h │ ├── SIMPBMS-BATTERY.cpp │ ├── SIMPBMS-BATTERY.h │ ├── SONO-BATTERY.cpp │ ├── SONO-BATTERY.h │ ├── TESLA-BATTERY.cpp │ ├── TESLA-BATTERY.h │ ├── TESLA-HTML.h │ ├── TEST-FAKE-BATTERY.cpp │ ├── TEST-FAKE-BATTERY.h │ ├── VOLVO-SPA-BATTERY.cpp │ ├── VOLVO-SPA-BATTERY.h │ ├── VOLVO-SPA-HTML.h │ ├── VOLVO-SPA-HYBRID-BATTERY.cpp │ ├── VOLVO-SPA-HYBRID-BATTERY.h │ └── VOLVO-SPA-HYBRID-HTML.h │ ├── charger │ ├── CHARGERS.cpp │ ├── CHARGERS.h │ ├── CHEVY-VOLT-CHARGER.cpp │ ├── CHEVY-VOLT-CHARGER.h │ ├── CanCharger.h │ ├── NISSAN-LEAF-CHARGER.cpp │ └── NISSAN-LEAF-CHARGER.h │ ├── communication │ ├── can │ │ ├── comm_can.cpp │ │ ├── comm_can.h │ │ ├── obd.cpp │ │ └── obd.h │ ├── contactorcontrol │ │ ├── comm_contactorcontrol.cpp │ │ └── comm_contactorcontrol.h │ ├── equipmentstopbutton │ │ ├── comm_equipmentstopbutton.cpp │ │ └── comm_equipmentstopbutton.h │ ├── nvm │ │ ├── comm_nvm.cpp │ │ └── comm_nvm.h │ ├── precharge_control │ │ ├── precharge_control.cpp │ │ └── precharge_control.h │ └── rs485 │ │ ├── comm_rs485.cpp │ │ └── comm_rs485.h │ ├── datalayer │ ├── datalayer.cpp │ ├── datalayer.h │ ├── datalayer_extended.cpp │ └── datalayer_extended.h │ ├── devboard │ ├── debug │ │ ├── debug.cfg │ │ ├── debug_custom.json │ │ └── esp32.svd │ ├── hal │ │ ├── hal.h │ │ ├── hw_3LB.h │ │ ├── hw_devkit.h │ │ ├── hw_lilygo.h │ │ └── hw_stark.h │ ├── mqtt │ │ ├── mqtt.cpp │ │ └── mqtt.h │ ├── safety │ │ ├── safety.cpp │ │ └── safety.h │ ├── sdcard │ │ ├── sdcard.cpp │ │ └── sdcard.h │ ├── utils │ │ ├── debounce_button.cpp │ │ ├── debounce_button.h │ │ ├── events.cpp │ │ ├── events.h │ │ ├── led_handler.cpp │ │ ├── led_handler.h │ │ ├── logging.cpp │ │ ├── logging.h │ │ ├── ntp_time.cpp │ │ ├── ntp_time.h │ │ ├── time_meas.h │ │ ├── timer.cpp │ │ ├── timer.h │ │ ├── types.cpp │ │ ├── types.h │ │ └── value_mapping.h │ ├── webserver │ │ ├── BatteryHtmlRenderer.h │ │ ├── advanced_battery_html.cpp │ │ ├── advanced_battery_html.h │ │ ├── can_logging_html.cpp │ │ ├── can_logging_html.h │ │ ├── can_replay_html.cpp │ │ ├── can_replay_html.h │ │ ├── cellmonitor_html.cpp │ │ ├── cellmonitor_html.h │ │ ├── debug_logging_html.cpp │ │ ├── debug_logging_html.h │ │ ├── events_html.cpp │ │ ├── events_html.h │ │ ├── index_html.cpp │ │ ├── index_html.h │ │ ├── settings_html.cpp │ │ ├── settings_html.h │ │ ├── webserver.cpp │ │ └── webserver.h │ └── wifi │ │ ├── wifi.cpp │ │ └── wifi.h │ ├── include.h │ ├── inverter │ ├── AFORE-CAN.cpp │ ├── AFORE-CAN.h │ ├── BYD-CAN.cpp │ ├── BYD-CAN.h │ ├── BYD-MODBUS.cpp │ ├── BYD-MODBUS.h │ ├── CanInverterProtocol.h │ ├── FERROAMP-CAN.cpp │ ├── FERROAMP-CAN.h │ ├── FOXESS-CAN.cpp │ ├── FOXESS-CAN.h │ ├── GROWATT-HV-CAN.cpp │ ├── GROWATT-HV-CAN.h │ ├── GROWATT-LV-CAN.cpp │ ├── GROWATT-LV-CAN.h │ ├── INVERTERS.cpp │ ├── INVERTERS.h │ ├── InverterProtocol.h │ ├── KOSTAL-RS485.cpp │ ├── KOSTAL-RS485.h │ ├── ModbusInverterProtocol.cpp │ ├── ModbusInverterProtocol.h │ ├── PYLON-CAN.cpp │ ├── PYLON-CAN.h │ ├── PYLON-LV-CAN.cpp │ ├── PYLON-LV-CAN.h │ ├── Rs485InverterProtocol.h │ ├── SCHNEIDER-CAN.cpp │ ├── SCHNEIDER-CAN.h │ ├── SMA-BYD-H-CAN.cpp │ ├── SMA-BYD-H-CAN.h │ ├── SMA-BYD-HVS-CAN.cpp │ ├── SMA-BYD-HVS-CAN.h │ ├── SMA-LV-CAN.cpp │ ├── SMA-LV-CAN.h │ ├── SMA-TRIPOWER-CAN.cpp │ ├── SMA-TRIPOWER-CAN.h │ ├── SOFAR-CAN.cpp │ ├── SOFAR-CAN.h │ ├── SOLAX-CAN.cpp │ ├── SOLAX-CAN.h │ ├── SUNGROW-CAN.cpp │ └── SUNGROW-CAN.h │ ├── lib │ ├── ESP32Async-ESPAsyncWebServer │ │ ├── CMakeLists.txt │ │ ├── CODE_OF_CONDUCT.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── library.json │ │ ├── library.properties │ │ └── src │ │ │ ├── AsyncEventSource.cpp │ │ │ ├── AsyncEventSource.h │ │ │ ├── AsyncJson.cpp │ │ │ ├── AsyncJson.h │ │ │ ├── AsyncMessagePack.cpp │ │ │ ├── AsyncMessagePack.h │ │ │ ├── AsyncWebHeader.cpp │ │ │ ├── AsyncWebServerVersion.h │ │ │ ├── AsyncWebSocket.cpp │ │ │ ├── AsyncWebSocket.h │ │ │ ├── BackPort_SHA1Builder.cpp │ │ │ ├── BackPort_SHA1Builder.h │ │ │ ├── ChunkPrint.cpp │ │ │ ├── ChunkPrint.h │ │ │ ├── ESPAsyncWebServer.h │ │ │ ├── Middleware.cpp │ │ │ ├── WebAuthentication.cpp │ │ │ ├── WebAuthentication.h │ │ │ ├── WebHandlerImpl.h │ │ │ ├── WebHandlers.cpp │ │ │ ├── WebRequest.cpp │ │ │ ├── WebResponseImpl.h │ │ │ ├── WebResponses.cpp │ │ │ ├── WebServer.cpp │ │ │ └── literals.h │ ├── YiannisBourkelis-Uptime-Library │ │ ├── LICENSE │ │ ├── README.md │ │ ├── library.properties │ │ └── src │ │ │ ├── uptime.cpp │ │ │ ├── uptime.h │ │ │ ├── uptime_formatter.cpp │ │ │ └── uptime_formatter.h │ ├── adafruit-Adafruit_NeoPixel │ │ ├── Adafruit_NeoPixel.cpp │ │ ├── Adafruit_NeoPixel.h │ │ └── esp.c │ ├── ayushsharma82-ElegantOTA │ │ ├── CurrentPlainHTML.txt │ │ ├── LICENSE │ │ ├── README.md │ │ ├── keywords.txt │ │ ├── library.json │ │ ├── library.properties │ │ ├── platformio_upload.py │ │ └── src │ │ │ ├── ElegantOTA.cpp │ │ │ ├── ElegantOTA.h │ │ │ ├── elop.cpp │ │ │ └── elop.h │ ├── bblanchon-ArduinoJson │ │ └── ArduinoJson.h │ ├── eModbus-eModbus │ │ ├── CoilData.cpp │ │ ├── CoilData.h │ │ ├── Logging.cpp │ │ ├── Logging.h │ │ ├── ModbusError.h │ │ ├── ModbusMessage.cpp │ │ ├── ModbusMessage.h │ │ ├── ModbusServer.cpp │ │ ├── ModbusServer.h │ │ ├── ModbusServerEthernet.h │ │ ├── ModbusServerRTU.cpp │ │ ├── ModbusServerRTU.h │ │ ├── ModbusTypeDefs.cpp │ │ ├── ModbusTypeDefs.h │ │ ├── RTUutils.cpp │ │ ├── RTUutils.h │ │ ├── options.h │ │ └── scripts │ │ │ ├── README.md │ │ │ ├── mbServerFCs.cpp │ │ │ └── mbServerFCs.h │ ├── mathieucarbou-AsyncTCPSock │ │ ├── LICENSE │ │ ├── library.json │ │ ├── library.properties │ │ └── src │ │ │ ├── AsyncTCP.cpp │ │ │ ├── AsyncTCP.h │ │ │ ├── AsyncTCP_SSL.h │ │ │ ├── AsyncTCP_SSL.hpp │ │ │ ├── AsyncTCP_TLS_Context.cpp │ │ │ └── AsyncTCP_TLS_Context.h │ ├── miwagner-ESP32-Arduino-CAN │ │ ├── CAN.c │ │ ├── CAN.h │ │ ├── CAN_config.h │ │ ├── ESP32CAN.cpp │ │ ├── ESP32CAN.h │ │ └── can_regdef.h │ ├── pierremolinaro-ACAN2517FD │ │ ├── ACAN2517FD.cpp │ │ ├── ACAN2517FD.h │ │ ├── ACAN2517FDFilters.h │ │ ├── ACAN2517FDSettings.cpp │ │ ├── ACAN2517FDSettings.h │ │ ├── ACANFDBuffer.h │ │ ├── ACANFD_DataBitRateFactor.h │ │ ├── CANFDMessage.h │ │ ├── CANMessage.h │ │ ├── LICENSE │ │ └── README.md │ └── pierremolinaro-acan2515 │ │ ├── ACAN2515.cpp │ │ ├── ACAN2515.h │ │ ├── ACAN2515Settings.cpp │ │ ├── ACAN2515Settings.h │ │ ├── ACAN2515_Buffer16.h │ │ ├── ACANBuffer.h │ │ ├── CANMessage.h │ │ └── MCP2515ReceiveFilters.h │ └── system_settings.h ├── cmake_clean.bat ├── min_spiffs.csv ├── platformio.ini └── test ├── CMakeLists.txt ├── microtest.h ├── test_lib.cpp ├── test_lib.h └── utils └── events_test.cpp_ /.clang-format: -------------------------------------------------------------------------------- 1 | # Google C/C++ Code Style settings 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | 4 | Language: Cpp 5 | BasedOnStyle: Google 6 | AccessModifierOffset: -1 7 | AlignAfterOpenBracket: Align 8 | AlignConsecutiveAssignments: None 9 | AlignOperands: Align 10 | AllowAllArgumentsOnNextLine: true 11 | AllowAllConstructorInitializersOnNextLine: true 12 | AllowAllParametersOfDeclarationOnNextLine: false 13 | AllowShortBlocksOnASingleLine: Empty 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: Inline 16 | AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding 17 | AllowShortLambdasOnASingleLine: Inline 18 | AllowShortLoopsOnASingleLine: false 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakTemplateDeclarations: Yes 21 | BinPackArguments: true 22 | BreakBeforeBraces: Custom 23 | BraceWrapping: 24 | AfterCaseLabel: false 25 | AfterClass: false 26 | AfterStruct: false 27 | AfterControlStatement: Never 28 | AfterEnum: false 29 | AfterFunction: false 30 | AfterNamespace: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | BeforeLambdaBody: false 36 | IndentBraces: false 37 | SplitEmptyFunction: false 38 | SplitEmptyRecord: false 39 | SplitEmptyNamespace: false 40 | BreakBeforeBinaryOperators: None 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializers: BeforeColon 43 | BreakInheritanceList: BeforeColon 44 | ColumnLimit: 120 45 | CompactNamespaces: false 46 | ContinuationIndentWidth: 4 47 | Cpp11BracedListStyle: true 48 | DerivePointerAlignment: false # Make sure the * or & align on the left 49 | EmptyLineBeforeAccessModifier: LogicalBlock 50 | FixNamespaceComments: true 51 | IncludeBlocks: Preserve 52 | IndentCaseLabels: true 53 | IndentPPDirectives: None 54 | IndentWidth: 2 55 | KeepEmptyLinesAtTheStartOfBlocks: true 56 | MaxEmptyLinesToKeep: 1 57 | NamespaceIndentation: None 58 | ObjCSpaceAfterProperty: false 59 | ObjCSpaceBeforeProtocolList: true 60 | PointerAlignment: Left 61 | ReflowComments: false 62 | # SeparateDefinitionBlocks: Always # Only support since clang-format 14 63 | SpaceAfterCStyleCast: false 64 | SpaceAfterLogicalNot: false 65 | SpaceAfterTemplateKeyword: true 66 | SpaceBeforeAssignmentOperators: true 67 | SpaceBeforeCpp11BracedList: false 68 | SpaceBeforeCtorInitializerColon: true 69 | SpaceBeforeInheritanceColon: true 70 | SpaceBeforeParens: ControlStatements 71 | SpaceBeforeRangeBasedForLoopColon: true 72 | SpaceBeforeSquareBrackets: false 73 | SpaceInEmptyParentheses: false 74 | SpacesBeforeTrailingComments: 2 75 | SpacesInAngles: false 76 | SpacesInCStyleCastParentheses: false 77 | SpacesInContainerLiterals: false 78 | SpacesInParentheses: false 79 | SpacesInSquareBrackets: false 80 | Standard: c++11 81 | TabWidth: 4 82 | UseTab: Never 83 | InsertNewlineAtEOF: true 84 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected Behavior 2 | 3 | 4 | 5 | ### Actual Behavior 6 | 7 | 8 | 9 | ### Steps to Reproduce the Problem 10 | 11 | 12 | 13 | 1. 14 | 1. 15 | 1. 16 | 17 | ### Settings 18 | 19 | 20 | 21 | - Software version: `` 22 | - Battery used: `` 23 | - Inverter communication protocol: `` 24 | - Hardware used for Battery-Emulator: `HW_LILYGO, HW_STARK, Custom` 25 | - CONTACTOR_CONTROL: `yes/no` 26 | - CAN_ADDON: `yes/no` 27 | - WEBSERVER: `yes/no` 28 | - MQTT: `yes/no` 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: Triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the encountered bug is 12 | 13 | **To Reproduce** 14 | Please try to explain the steps required to reproduce the issue 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Version and settings (please complete the following information):** 23 | - Please attach your USER_SETTINGS files for easier debugging 24 | - Software version: X.Y.Z 25 | - Battery used: [NISSAN_LEAF] 26 | - Inverter communication protocol: [BYD_MODBUS] 27 | - Hardware used for Battery-Emulator: [HW_LILYGO/HW_STARK/HW_DEVKIT] 28 | - CONTACTOR_CONTROL: [yes/no] 29 | - MQTT: [yes/no] 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Discord 4 | url: https://www.patreon.com/dala 5 | about: Get direct support and hang out with us! Join via Patreon 6 | - name: Wiki 7 | url: https://github.com/dalathegreat/Battery-Emulator/wiki 8 | about: Highly recommended to read before asking questions! 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature request] " 5 | labels: Feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What 2 | This PR implements ... 3 | 4 | ### Why 5 | Why does it do it? 6 | 7 | ### How 8 | How does it do it? 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot checks for new versions of the dependencies used in the GitHub Actions used in this repository. 2 | # Dependabot will raise pull requests for version updates for any outdated actions that it finds. After the initial 3 | # version updates, Dependabot will continue to check for outdated versions of actions according to the schedule defined. 4 | 5 | version: 2 6 | updates: 7 | # Enable version updates for GitHub Actions 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | # Check for updates to GitHub Actions every week 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.github/workflows/run-pre-commit.yml: -------------------------------------------------------------------------------- 1 | # This GithHub Action runs the pre-commit defined in the .pre-commit-config.yaml file 2 | 3 | name: 📝 Run pre-commit 4 | 5 | on: 6 | - push 7 | - pull_request 8 | 9 | jobs: 10 | pre-commit: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Run pre-commit 17 | uses: pre-commit/action@v3.0.1 18 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: ⚙️ Run Unit Tests 2 | 3 | 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Configure and build with CMake 15 | run: | 16 | mkdir build 17 | cd build 18 | cmake .. 19 | cmake --build . 20 | 21 | - name: Run unit tests 22 | run: | 23 | set -e # Exit immediately on non-zero exit code 24 | cd build/test 25 | dir -s 26 | for test_executable in *; do 27 | if [ -f "$test_executable" ] && [ -x "$test_executable" ]; then 28 | ./"$test_executable" 29 | fi 30 | done 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore any .vscode folder 2 | *.vscode/ 3 | 4 | # Ignore any files in any build folder 5 | *build/ 6 | 7 | # Ignore .exe (unit tests) 8 | *.exe 9 | **/.DS_Store 10 | 11 | #ignore platformio 12 | .pioenvs 13 | .piolibdeps 14 | .clang_complete 15 | .gcc-flags.json 16 | .pio 17 | logs 18 | 19 | # Ignore upload command (removes the need to re-compile after Verify when running Download, in e.g. VS Code) 20 | upload.bat 21 | compile.bat 22 | 23 | # Ignore binary files 24 | *.bin 25 | 26 | # Ignore secret file 27 | USER_SECRETS.h -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # This pre-commit configuration file configures clang-format to run upon running the command line command 'git commit'. 2 | # The pre-commit git hook is installed using the command line command 'pre-commit install'. 3 | 4 | exclude: '^Software/src/lib/' # don't run hooks on external libraries 5 | 6 | # This continuous integration section is optional, and enables autoupdating the pre-commit configuration, ensuring that 7 | # the hook versions are kept up to date. 8 | ci: 9 | autoupdate_schedule: monthly 10 | 11 | repos: 12 | - repo: https://github.com/pre-commit/mirrors-clang-format 13 | rev: v20.1.5 14 | hooks: 15 | - id: clang-format 16 | args: [-Werror] # change formatting warnings to errors, hook includes -i (Inplace edit) by default 17 | types_or: [c++, c] # override default file types to only C and CPP files 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # Set the C++ standard to C++20 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | project(BatteryEmulator) 8 | 9 | # add_subdirectory(Software/src/devboard/utils) 10 | add_subdirectory(test) 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing to the Battery-Emulator project 2 | 3 | What can I do? 4 | -------------- 5 | 6 | **"Help - I want to contribute something, but I don't know what?"** 7 | 8 | You're in luck. There's various sources to contribute: 9 | - Improve the [Wiki documentation](https://github.com/dalathegreat/Battery-Emulator/wiki) 10 | - Especially battery/inverter specific pages need updating. Attach pictures of batteries, wiring diagrams, helpful info etc. 11 | - Have a look at the [issue tracker](https://github.com/dalathegreat/Battery-Emulator/issues), especially issues with labels: 12 | - [good first issue](https://github.com/dalathegreat/Battery-Emulator/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)! 13 | - Use your favorite text editor to find `TODO` comments in the code 14 | - Ask us! 15 | - [Discussion page](https://github.com/dalathegreat/Battery-Emulator/discussions) 16 | - [Discord server](https://www.patreon.com/dala) 17 | 18 | ## Notes on embedded system 19 | The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the FUNCTION_TIME_MEASUREMENT option in the USER_SETTINGS.h file 20 | 21 | ## Code formatting 22 | The project enforces a specific code formatting in the workflows. To get your code formatted properly, it is easiest to use a pre-commit hook before pushing the code to a pull request. 23 | 24 | Before you begin, make sure you have installed Python on the system! 25 | To install the pre-commit, open the repository via Git Bash/CMD, and run 26 | ``` 27 | pip install pre-commit 28 | ``` 29 | And then run 30 | ``` 31 | pre-commit install 32 | ``` 33 | Then you can run the autoformat any time with the command 34 | ``` 35 | pre-commit 36 | ``` 37 | Or force it to check all files with 38 | ``` 39 | pre-commit run --all-files 40 | ``` 41 | -------------------------------------------------------------------------------- /Software/USER_SECRETS.TEMPLATE.h: -------------------------------------------------------------------------------- 1 | /* This file should be renamed to USER_SECRETS.h to be able to use the software! 2 | It contains all the credentials that should never be made public */ 3 | 4 | //Password to the access point generated by the Battery-Emulator 5 | #define AP_PASSWORD "123456789" // Minimum of 8 characters; set to blank if you want the access point to be open 6 | 7 | //Name and password of Wifi network you want the emulator to connect to 8 | #define WIFI_SSID "REPLACE_WITH_YOUR_SSID" // Maximum of 63 characters 9 | #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD" // Minimum of 8 characters 10 | 11 | //Set WEBSERVER_AUTH_REQUIRED to true to require a password when accessing the webserver homepage. Improves cybersecurity. 12 | #define WEBSERVER_AUTH_REQUIRED false 13 | #define HTTP_USERNAME "admin" // Username for webserver authentication 14 | #define HTTP_PASSWORD "admin" // Password for webserver authentication 15 | 16 | //MQTT credentials 17 | #define MQTT_SERVER "192.168.xxx.yyy" // MQTT server address 18 | #define MQTT_PORT 1883 // MQTT server port 19 | #define MQTT_USER "" // MQTT username, leave blank for no authentication 20 | #define MQTT_PASSWORD "" // MQTT password, leave blank for no authentication 21 | -------------------------------------------------------------------------------- /Software/src/battery/BATTERIES.cpp: -------------------------------------------------------------------------------- 1 | #include "../include.h" 2 | 3 | #include "../datalayer/datalayer_extended.h" 4 | #include "CanBattery.h" 5 | #include "RS485Battery.h" 6 | 7 | // These functions adapt the old C-style global functions battery-API to the 8 | // object-oriented battery API. 9 | 10 | // The instantiated class is defined by the pre-compiler define 11 | // to support battery class selection at compile-time 12 | #ifdef SELECTED_BATTERY_CLASS 13 | 14 | Battery* battery = nullptr; 15 | Battery* battery2 = nullptr; 16 | 17 | void setup_battery() { 18 | // Instantiate the battery only once just in case this function gets called multiple times. 19 | if (battery == nullptr) { 20 | battery = new SELECTED_BATTERY_CLASS(); 21 | } 22 | battery->setup(); 23 | 24 | #ifdef DOUBLE_BATTERY 25 | if (battery2 == nullptr) { 26 | #if defined(BMW_I3_BATTERY) 27 | battery2 = 28 | new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing, 29 | can_config.battery_double, WUP_PIN2); 30 | #elif defined(KIA_HYUNDAI_64_BATTERY) 31 | battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2, 32 | &datalayer.system.status.battery2_allowed_contactor_closing, 33 | can_config.battery_double); 34 | #elif defined(SANTA_FE_PHEV_BATTERY) || defined(TEST_FAKE_BATTERY) 35 | battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, can_config.battery_double); 36 | #else 37 | battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, nullptr, can_config.battery_double); 38 | #endif 39 | } 40 | battery2->setup(); 41 | #endif 42 | } 43 | 44 | void update_values_battery() { 45 | battery->update_values(); 46 | } 47 | 48 | // transmit_can_battery is called once and we need to 49 | // call both batteries. 50 | void transmit_can_battery(unsigned long currentMillis) { 51 | ((CanBattery*)battery)->transmit_can(currentMillis); 52 | 53 | #ifdef DOUBLE_BATTERY 54 | ((CanBattery*)battery2)->transmit_can(currentMillis); 55 | #endif 56 | } 57 | 58 | void handle_incoming_can_frame_battery(CAN_frame rx_frame) { 59 | ((CanBattery*)battery)->handle_incoming_can_frame(rx_frame); 60 | } 61 | 62 | #ifdef DOUBLE_BATTERY 63 | void update_values_battery2() { 64 | battery2->update_values(); 65 | } 66 | 67 | void handle_incoming_can_frame_battery2(CAN_frame rx_frame) { 68 | ((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame); 69 | } 70 | #endif 71 | 72 | #ifdef RS485_BATTERY_SELECTED 73 | void transmit_rs485(unsigned long currentMillis) { 74 | ((RS485Battery*)battery)->transmit_rs485(currentMillis); 75 | } 76 | 77 | void receive_RS485() { 78 | ((RS485Battery*)battery)->receive_RS485(); 79 | } 80 | 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /Software/src/battery/BMW-SBOX.h: -------------------------------------------------------------------------------- 1 | #ifndef BMW_SBOX_CONTROL_H 2 | #define BMW_SBOX_CONTROL_H 3 | #include "../include.h" 4 | #define CAN_SHUNT_SELECTED 5 | void transmit_can(CAN_frame* tx_frame, int interface); 6 | 7 | /** Minimum input voltage required to enable relay control **/ 8 | #define MINIMUM_INPUT_VOLTAGE 250 9 | 10 | /** Minimum required percentage of input voltage at the output port to engage the positive relay. **/ 11 | /** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/ 12 | #define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99 13 | 14 | /* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system. 15 | * t=3RC at minimum, t=5RC ideally 16 | */ 17 | 18 | #define CONTACTOR_CONTROL_T1 5000 // Time before negative contactor engages and precharging starts 19 | #define CONTACTOR_CONTROL_T2 5000 // Precharge time before precharge resistor is bypassed by positive contactor 20 | #define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /Software/src/battery/BYD-ATTO-3-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _BYD_ATTO_3_HTML_H 2 | #define _BYD_ATTO_3_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class BydAtto3HtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | BydAtto3HtmlRenderer(DATALAYER_INFO_BYDATTO3* dl) : byd_datalayer(dl) {} 11 | 12 | String get_status_html() { 13 | String content; 14 | 15 | static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"}; 16 | content += "

SOC method used: " + String(SOCmethod[byd_datalayer->SOC_method]) + "

"; 17 | content += "

SOC estimated: " + String(byd_datalayer->SOC_estimated) + "

"; 18 | content += "

SOC highprec: " + String(byd_datalayer->SOC_highprec) + "

"; 19 | content += "

SOC OBD2: " + String(byd_datalayer->SOC_polled) + "

"; 20 | content += "

Voltage periodic: " + String(byd_datalayer->voltage_periodic) + "

"; 21 | content += "

Voltage OBD2: " + String(byd_datalayer->voltage_polled) + "

"; 22 | content += "

Temperature sensor 1: " + String(byd_datalayer->battery_temperatures[0]) + "

"; 23 | content += "

Temperature sensor 2: " + String(byd_datalayer->battery_temperatures[1]) + "

"; 24 | content += "

Temperature sensor 3: " + String(byd_datalayer->battery_temperatures[2]) + "

"; 25 | content += "

Temperature sensor 4: " + String(byd_datalayer->battery_temperatures[3]) + "

"; 26 | content += "

Temperature sensor 5: " + String(byd_datalayer->battery_temperatures[4]) + "

"; 27 | content += "

Temperature sensor 6: " + String(byd_datalayer->battery_temperatures[5]) + "

"; 28 | content += "

Temperature sensor 7: " + String(byd_datalayer->battery_temperatures[6]) + "

"; 29 | content += "

Temperature sensor 8: " + String(byd_datalayer->battery_temperatures[7]) + "

"; 30 | content += "

Temperature sensor 9: " + String(byd_datalayer->battery_temperatures[8]) + "

"; 31 | content += "

Temperature sensor 10: " + String(byd_datalayer->battery_temperatures[9]) + "

"; 32 | content += "

Unknown0: " + String(byd_datalayer->unknown0) + "

"; 33 | content += "

Unknown1: " + String(byd_datalayer->unknown1) + "

"; 34 | content += "

Charge power raw: " + String(byd_datalayer->chargePower) + "

"; 35 | content += "

Unknown3: " + String(byd_datalayer->unknown3) + "

"; 36 | content += "

Unknown4: " + String(byd_datalayer->unknown4) + "

"; 37 | content += "

Unknown5: " + String(byd_datalayer->unknown5) + "

"; 38 | content += "

Unknown6: " + String(byd_datalayer->unknown6) + "

"; 39 | content += "

Unknown7: " + String(byd_datalayer->unknown7) + "

"; 40 | content += "

Unknown8: " + String(byd_datalayer->unknown8) + "

"; 41 | content += "

Unknown9: " + String(byd_datalayer->unknown9) + "

"; 42 | content += "

Unknown10: " + String(byd_datalayer->unknown10) + "

"; 43 | content += "

Unknown11: " + String(byd_datalayer->unknown11) + "

"; 44 | content += "

Unknown12: " + String(byd_datalayer->unknown12) + "

"; 45 | content += "

Unknown13: " + String(byd_datalayer->unknown12) + "

"; 46 | 47 | return content; 48 | } 49 | 50 | private: 51 | DATALAYER_INFO_BYDATTO3* byd_datalayer; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Software/src/battery/Battery.h: -------------------------------------------------------------------------------- 1 | #ifndef BATTERY_H 2 | #define BATTERY_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 6 | 7 | // Abstract base class for next-generation battery implementations. 8 | // Defines the interface to call battery specific functionality. 9 | class Battery { 10 | public: 11 | virtual void setup(void) = 0; 12 | virtual void update_values() = 0; 13 | 14 | // The name of the comm interface the battery is using. 15 | virtual String interface_name() = 0; 16 | 17 | // These are commands from external I/O (UI, MQTT etc.) 18 | // Override in battery if it supports them. Otherwise they are NOP. 19 | 20 | virtual bool supports_clear_isolation() { return false; } 21 | virtual bool supports_reset_BMS() { return false; } 22 | virtual bool supports_reset_crash() { return false; } 23 | virtual bool supports_reset_NVROL() { return false; } 24 | virtual bool supports_reset_DTC() { return false; } 25 | virtual bool supports_read_DTC() { return false; } 26 | virtual bool supports_reset_SOH() { return false; } 27 | virtual bool supports_reset_BECM() { return false; } 28 | virtual bool supports_contactor_close() { return false; } 29 | virtual bool supports_set_fake_voltage() { return false; } 30 | virtual bool supports_manual_balancing() { return false; } 31 | virtual bool supports_real_BMS_status() { return false; } 32 | 33 | virtual void clear_isolation() {} 34 | virtual void reset_BMS() {} 35 | virtual void reset_crash() {} 36 | virtual void reset_NVROL() {} 37 | virtual void reset_DTC() {} 38 | virtual void read_DTC() {} 39 | virtual void reset_SOH() {} 40 | virtual void reset_BECM() {} 41 | virtual void request_open_contactors() {} 42 | virtual void request_close_contactors() {} 43 | 44 | virtual void set_fake_voltage(float v) {} 45 | virtual float get_voltage() { static_cast(datalayer.battery.status.voltage_dV) / 10.0; } 46 | 47 | // This allows for battery specific SOC plausibility calculations to be performed. 48 | virtual bool soc_plausible() { return true; } 49 | 50 | virtual BatteryHtmlRenderer& get_status_renderer() { return defaultRenderer; } 51 | 52 | private: 53 | BatteryDefaultRenderer defaultRenderer; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /Software/src/battery/CHADEMO-SHUNTS.h: -------------------------------------------------------------------------------- 1 | #ifndef CHADEMO_SHUNTS_H 2 | #define CHADEMO_SHUNTS_H 3 | 4 | uint16_t get_measured_voltage(); 5 | uint16_t get_measured_current(); 6 | void ISA_handleFrame(CAN_frame* frame); 7 | inline void ISA_handle521(CAN_frame* frame); 8 | inline void ISA_handle522(CAN_frame* frame); 9 | inline void ISA_handle523(CAN_frame* frame); 10 | inline void ISA_handle524(CAN_frame* frame); 11 | inline void ISA_handle525(CAN_frame* frame); 12 | inline void ISA_handle526(CAN_frame* frame); 13 | inline void ISA_handle527(CAN_frame* frame); 14 | inline void ISA_handle528(CAN_frame* frame); 15 | void ISA_initialize(); 16 | void ISA_STOP(); 17 | void ISA_sendSTORE(); 18 | void ISA_START(); 19 | void ISA_RESTART(); 20 | void ISA_deFAULT(); 21 | void ISA_initCurrent(); 22 | void ISA_getCONFIG(uint8_t i); 23 | void ISA_getCAN_ID(uint8_t i); 24 | void ISA_getINFO(uint8_t i); 25 | 26 | void transmit_can_frame(CAN_frame* tx_frame, int interface); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Software/src/battery/CMFA-EV-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _CMFA_EV_HTML_H 2 | #define _CMFA_EV_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class CmfaEvHtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | String get_status_html() { 11 | String content; 12 | 13 | content += "

SOC U: " + String(datalayer_extended.CMFAEV.soc_u) + "percent

"; 14 | content += "

SOC Z: " + String(datalayer_extended.CMFAEV.soc_z) + "percent

"; 15 | content += "

SOH Average: " + String(datalayer_extended.CMFAEV.soh_average) + "pptt

"; 16 | content += "

12V voltage: " + String(datalayer_extended.CMFAEV.lead_acid_voltage) + "mV

"; 17 | content += "

Highest cell number: " + String(datalayer_extended.CMFAEV.highest_cell_voltage_number) + "

"; 18 | content += "

Lowest cell number: " + String(datalayer_extended.CMFAEV.lowest_cell_voltage_number) + "

"; 19 | content += "

Max regen power: " + String(datalayer_extended.CMFAEV.max_regen_power) + "

"; 20 | content += "

Max discharge power: " + String(datalayer_extended.CMFAEV.max_discharge_power) + "

"; 21 | content += "

Max charge power: " + String(datalayer_extended.CMFAEV.maximum_charge_power) + "

"; 22 | content += "

SOH available power: " + String(datalayer_extended.CMFAEV.SOH_available_power) + "

"; 23 | content += "

SOH generated power: " + String(datalayer_extended.CMFAEV.SOH_generated_power) + "

"; 24 | content += "

Average temperature: " + String(datalayer_extended.CMFAEV.average_temperature) + "dC

"; 25 | content += "

Maximum temperature: " + String(datalayer_extended.CMFAEV.maximum_temperature) + "dC

"; 26 | content += "

Minimum temperature: " + String(datalayer_extended.CMFAEV.minimum_temperature) + "dC

"; 27 | content += 28 | "

Cumulative energy discharged: " + String(datalayer_extended.CMFAEV.cumulative_energy_when_discharging) + 29 | "Wh

"; 30 | content += "

Cumulative energy charged: " + String(datalayer_extended.CMFAEV.cumulative_energy_when_charging) + 31 | "Wh

"; 32 | content += 33 | "

Cumulative energy regen: " + String(datalayer_extended.CMFAEV.cumulative_energy_in_regen) + "Wh

"; 34 | 35 | return content; 36 | } 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Software/src/battery/CanBattery.h: -------------------------------------------------------------------------------- 1 | #ifndef CAN_BATTERY_H 2 | #define CAN_BATTERY_H 3 | 4 | #include "Battery.h" 5 | 6 | #include "src/devboard/utils/types.h" 7 | 8 | // Abstract base class for batteries using the CAN bus 9 | class CanBattery : public Battery { 10 | public: 11 | virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; 12 | virtual void transmit_can(unsigned long currentMillis) = 0; 13 | 14 | String interface_name() { return getCANInterfaceName(can_interface); } 15 | 16 | protected: 17 | CAN_Interface can_interface; 18 | 19 | CanBattery() { can_interface = can_config.battery; } 20 | 21 | CanBattery(CAN_Interface interface) { can_interface = interface; } 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /Software/src/battery/DALY-BMS.h: -------------------------------------------------------------------------------- 1 | #ifndef DALY_BMS_H 2 | #define DALY_BMS_H 3 | 4 | #include "RS485Battery.h" 5 | 6 | /* Tweak these according to your battery build */ 7 | #define CELL_COUNT 14 8 | #define MAX_PACK_VOLTAGE_DV 580 //580 = 58.0V 9 | #define MIN_PACK_VOLTAGE_DV 460 //480 = 48.0V 10 | #define MAX_CELL_VOLTAGE_MV 4200 //Battery is put into emergency stop if one cell goes over this value 11 | #define MIN_CELL_VOLTAGE_MV 3200 //Battery is put into emergency stop if one cell goes below this value 12 | #define POWER_PER_PERCENT 50 // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...) 13 | #define POWER_PER_DEGREE_C 60 // max power added/removed per degree above/below 0°C 14 | #define POWER_AT_0_DEGREE_C 800 // power at 0°C 15 | 16 | /* Do not modify any rows below*/ 17 | #define BATTERY_SELECTED 18 | #define RS485_BATTERY_SELECTED 19 | #define SELECTED_BATTERY_CLASS DalyBms 20 | 21 | class DalyBms : public RS485Battery { 22 | public: 23 | void setup(); 24 | void update_values(); 25 | void transmit_rs485(unsigned long currentMillis); 26 | void receive_RS485(); 27 | 28 | private: 29 | int baud_rate() { return 9600; } 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /Software/src/battery/ECMP-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef STELLANTIS_ECMP_BATTERY_H 2 | #define STELLANTIS_ECMP_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | #include "ECMP-HTML.h" 8 | 9 | #define BATTERY_SELECTED 10 | #define SELECTED_BATTERY_CLASS EcmpBattery 11 | 12 | class EcmpBattery : public CanBattery { 13 | public: 14 | virtual void setup(void); 15 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 16 | virtual void update_values(); 17 | virtual void transmit_can(unsigned long currentMillis); 18 | 19 | BatteryHtmlRenderer& get_status_renderer() { return renderer; } 20 | 21 | private: 22 | EcmpHtmlRenderer renderer; 23 | static const int MAX_PACK_VOLTAGE_DV = 4546; 24 | static const int MIN_PACK_VOLTAGE_DV = 3210; 25 | static const int MAX_CELL_DEVIATION_MV = 100; 26 | static const int MAX_CELL_VOLTAGE_MV = 4250; 27 | static const int MIN_CELL_VOLTAGE_MV = 2700; 28 | 29 | bool battery_RelayOpenRequest = false; 30 | uint8_t counter_20ms = 0; 31 | uint8_t battery_MainConnectorState = 0; 32 | int16_t battery_current = 0; 33 | uint16_t battery_voltage = 370; 34 | uint16_t battery_soc = 0; 35 | uint16_t cellvoltages[108]; 36 | uint16_t battery_AllowedMaxChargeCurrent = 0; 37 | uint16_t battery_AllowedMaxDischargeCurrent = 0; 38 | uint16_t battery_insulationResistanceKOhm = 0; 39 | int16_t battery_highestTemperature = 0; 40 | int16_t battery_lowestTemperature = 0; 41 | 42 | unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent 43 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent 44 | 45 | CAN_frame ECMP_382 = { 46 | .FD = false, //BSI_Info (VCU) PSA specific 47 | .ext_ID = false, 48 | .DLC = 8, 49 | .ID = 0x382, 50 | .data = {0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //09 20 on AC charge. 0A 20 on DC charge 51 | CAN_frame ECMP_0F0 = {.FD = false, //VCU (Common) 52 | .ext_ID = false, 53 | .DLC = 8, 54 | .ID = 0x0F0, 55 | .data = {0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}}; 56 | uint8_t data_0F0_20[16] = {0xFF, 0x0E, 0x1D, 0x2C, 0x3B, 0x4A, 0x59, 0x68, 57 | 0x77, 0x86, 0x95, 0xA4, 0xB3, 0xC2, 0xD1, 0xE0}; 58 | uint8_t data_0F0_00[16] = {0xF1, 0x00, 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A, 59 | 0x79, 0x88, 0x97, 0xA6, 0xB5, 0xC4, 0xD3, 0xE2}; 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Software/src/battery/ECMP-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _ECMP_HTML_H 2 | #define _ECMP_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class EcmpHtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | String get_status_html() { 11 | String content; 12 | 13 | content += "

Main Connector State: "; 14 | if (datalayer_extended.stellantisECMP.MainConnectorState == 0) { 15 | content += "Contactors open

"; 16 | } else if (datalayer_extended.stellantisECMP.MainConnectorState == 0x01) { 17 | content += "Precharged"; 18 | } else { 19 | content += "Invalid"; 20 | } 21 | content += 22 | "

Insulation Resistance: " + String(datalayer_extended.stellantisECMP.InsulationResistance) + "kOhm

"; 23 | 24 | return content; 25 | } 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Software/src/battery/IMIEV-CZERO-ION-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef IMIEV_CZERO_ION_BATTERY_H 2 | #define IMIEV_CZERO_ION_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS ImievCZeroIonBattery 10 | 11 | class ImievCZeroIonBattery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | static const int MAX_PACK_VOLTAGE_DV = 3696; //5000 = 500.0V 20 | static const int MIN_PACK_VOLTAGE_DV = 3160; 21 | static const int MAX_CELL_DEVIATION_MV = 250; 22 | static const int MAX_CELL_VOLTAGE_MV = 4150; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2750; //Battery is put into emergency stop if one cell goes below this value 24 | 25 | uint8_t errorCode = 0; //stores if we have an error code active from battery control logic 26 | uint8_t BMU_Detected = 0; 27 | uint8_t CMU_Detected = 0; 28 | 29 | unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent 30 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent 31 | 32 | int pid_index = 0; 33 | int cmu_id = 0; 34 | int voltage_index = 0; 35 | int temp_index = 0; 36 | uint8_t BMU_SOC = 0; 37 | int temp_value = 0; 38 | double temp1 = 0; 39 | double temp2 = 0; 40 | double temp3 = 0; 41 | double voltage1 = 0; 42 | double voltage2 = 0; 43 | double BMU_Current = 0; 44 | double BMU_PackVoltage = 0; 45 | double BMU_Power = 0; 46 | double cell_voltages[88]; //array with all the cellvoltages 47 | double cell_temperatures[88]; //array with all the celltemperatures 48 | double max_volt_cel = 3.70; 49 | double min_volt_cel = 3.70; 50 | double max_temp_cel = 20.00; 51 | double min_temp_cel = 19.00; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Software/src/battery/JAGUAR-IPACE-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef JAGUAR_IPACE_BATTERY_H 2 | #define JAGUAR_IPACE_BATTERY_H 3 | 4 | #include "CanBattery.h" 5 | 6 | #define BATTERY_SELECTED 7 | #define SELECTED_BATTERY_CLASS JaguarIpaceBattery 8 | 9 | #define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V 10 | #define MIN_PACK_VOLTAGE_DV 3370 11 | #define MAX_CELL_DEVIATION_MV 250 12 | #define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value 13 | #define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value 14 | 15 | class JaguarIpaceBattery : public CanBattery { 16 | public: 17 | virtual void setup(void); 18 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 19 | virtual void update_values(); 20 | virtual void transmit_can(unsigned long currentMillis); 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Software/src/battery/KIA-E-GMP-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef KIA_E_GMP_BATTERY_H 2 | #define KIA_E_GMP_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | #include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" 6 | #include "CanBattery.h" 7 | 8 | extern ACAN2517FD canfd; 9 | 10 | #define ESTIMATE_SOC_FROM_CELLVOLTAGE 11 | 12 | #define BATTERY_SELECTED 13 | #define SELECTED_BATTERY_CLASS KiaEGmpBattery 14 | 15 | class KiaEGmpBattery : public CanBattery { 16 | public: 17 | virtual void setup(void); 18 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 19 | virtual void update_values(); 20 | virtual void transmit_can(unsigned long currentMillis); 21 | 22 | private: 23 | uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); 24 | void set_voltage_minmax_limits(); 25 | 26 | static const int MAX_PACK_VOLTAGE_DV = 8064; //5000 = 500.0V 27 | static const int MIN_PACK_VOLTAGE_DV = 4320; 28 | static const int MAX_CELL_DEVIATION_MV = 150; 29 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 30 | static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value 31 | static const int MAXCHARGEPOWERALLOWED = 10000; 32 | static const int MAXDISCHARGEPOWERALLOWED = 10000; 33 | static const int RAMPDOWN_SOC = 9000; // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00% 34 | static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing 35 | 36 | // Used for SoC compensation - Define internal resistance value in milliohms for the entire pack 37 | // How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance 38 | static const int PACK_INTERNAL_RESISTANCE_MOHM = 200; // 200 milliohms for the whole pack 39 | 40 | unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send 41 | unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send 42 | 43 | uint16_t inverterVoltageFrameHigh = 0; 44 | uint16_t inverterVoltage = 0; 45 | uint16_t soc_calculated = 0; 46 | uint16_t SOC_BMS = 0; 47 | uint16_t SOC_Display = 0; 48 | uint16_t SOC_estimated_lowest = 0; 49 | uint16_t SOC_estimated_highest = 0; 50 | uint16_t batterySOH = 1000; 51 | uint16_t CellVoltMax_mV = 3700; 52 | uint16_t CellVoltMin_mV = 3700; 53 | uint16_t batteryVoltage = 6700; 54 | int16_t leadAcidBatteryVoltage = 120; 55 | int16_t batteryAmps = 0; 56 | int16_t temperatureMax = 0; 57 | int16_t temperatureMin = 0; 58 | int16_t allowedDischargePower = 0; 59 | int16_t allowedChargePower = 0; 60 | int16_t poll_data_pid = 0; 61 | uint8_t CellVmaxNo = 0; 62 | uint8_t CellVminNo = 0; 63 | uint8_t batteryManagementMode = 0; 64 | uint8_t BMS_ign = 0; 65 | uint8_t batteryRelay = 0; 66 | uint8_t waterleakageSensor = 164; 67 | bool startedUp = false; 68 | bool ok_start_polling_battery = false; 69 | uint8_t counter_200 = 0; 70 | uint8_t KIA_7E4_COUNTER = 0x01; 71 | int8_t temperature_water_inlet = 0; 72 | int8_t powerRelayTemperature = 0; 73 | int8_t heatertemp = 0; 74 | bool set_voltage_limits = false; 75 | uint8_t ticks_200ms_counter = 0; 76 | uint8_t EGMP_1CF_counter = 0; 77 | uint8_t EGMP_3XF_counter = 0; 78 | }; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /Software/src/battery/KIA-HYUNDAI-64-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _KIA_HYUNDAI_64_HTML_H 2 | #define _KIA_HYUNDAI_64_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class KiaHyundai64HtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | KiaHyundai64HtmlRenderer(DATALAYER_INFO_KIAHYUNDAI64* dl) : kia_datalayer(dl) {} 11 | 12 | String get_status_html() { 13 | String content; 14 | 15 | auto print_hyundai = [&content](DATALAYER_INFO_KIAHYUNDAI64& data) { 16 | content += "

Cells: " + String(data.total_cell_count) + "S

"; 17 | content += "

12V voltage: " + String(data.battery_12V / 10.0, 1) + "

"; 18 | content += "

Waterleakage: " + String(data.waterleakageSensor) + "

"; 19 | content += "

Temperature, water inlet: " + String(data.temperature_water_inlet) + "

"; 20 | content += "

Temperature, power relay: " + String(data.powerRelayTemperature) + "

"; 21 | content += "

Batterymanagement mode: " + String(data.batteryManagementMode) + "

"; 22 | content += "

BMS ignition: " + String(data.BMS_ign) + "

"; 23 | content += "

Battery relay: " + String(data.batteryRelay) + "

"; 24 | }; 25 | 26 | print_hyundai(*kia_datalayer); 27 | 28 | return content; 29 | } 30 | 31 | private: 32 | DATALAYER_INFO_KIAHYUNDAI64* kia_datalayer; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef KIA_HYUNDAI_HYBRID_BATTERY_H 2 | #define KIA_HYUNDAI_HYBRID_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery 10 | 11 | class KiaHyundaiHybridBattery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | static const int MAX_PACK_VOLTAGE_DV = 2550; //5000 = 500.0V 20 | static const int MIN_PACK_VOLTAGE_DV = 1700; 21 | static const int MAX_CELL_DEVIATION_MV = 100; 22 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 24 | 25 | unsigned long previousMillis1000 = 0; // will store last time a 100ms CAN Message was send 26 | 27 | uint16_t SOC = 0; 28 | uint16_t SOC_display = 0; 29 | bool interlock_missing = false; 30 | int16_t battery_current = 0; 31 | uint8_t battery_current_high_byte = 0; 32 | uint16_t battery_voltage = 0; 33 | uint32_t available_charge_power = 0; 34 | uint32_t available_discharge_power = 0; 35 | int8_t battery_module_max_temperature = 0; 36 | int8_t battery_module_min_temperature = 0; 37 | uint8_t poll_data_pid = 0; 38 | uint16_t cellvoltages_mv[98]; 39 | uint16_t min_cell_voltage_mv = 3700; 40 | uint16_t max_cell_voltage_mv = 3700; 41 | 42 | CAN_frame KIA_7E4_id1 = {.FD = false, 43 | .ext_ID = false, 44 | .DLC = 8, 45 | .ID = 0x7E4, 46 | .data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}}; 47 | CAN_frame KIA_7E4_id2 = {.FD = false, 48 | .ext_ID = false, 49 | .DLC = 8, 50 | .ID = 0x7E4, 51 | .data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}}; 52 | CAN_frame KIA_7E4_id3 = {.FD = false, 53 | .ext_ID = false, 54 | .DLC = 8, 55 | .ID = 0x7E4, 56 | .data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; 57 | CAN_frame KIA_7E4_id5 = {.FD = false, 58 | .ext_ID = false, 59 | .DLC = 8, 60 | .ID = 0x7E4, 61 | .data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}}; 62 | CAN_frame KIA_7E4_ack = {.FD = false, 63 | .ext_ID = false, 64 | .DLC = 8, 65 | .ID = 0x7E4, //Ack frame, correct PID is returned. Flow control message 66 | .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Software/src/battery/MG-5-BATTERY.cpp: -------------------------------------------------------------------------------- 1 | #include "../include.h" 2 | #ifdef MG_5_BATTERY_H 3 | #include "../communication/can/comm_can.h" 4 | #include "../datalayer/datalayer.h" 5 | #include "../devboard/utils/events.h" 6 | #include "MG-5-BATTERY.h" 7 | 8 | /* TODO: 9 | - Get contactor closing working 10 | - Figure out which CAN messages need to be sent towards the battery to keep it alive 11 | - Map all values from battery CAN messages 12 | - Most important ones 13 | */ 14 | 15 | void Mg5Battery:: 16 | update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus 17 | 18 | datalayer.battery.status.real_soc; 19 | 20 | datalayer.battery.status.voltage_dV; 21 | 22 | datalayer.battery.status.current_dA; 23 | 24 | datalayer.battery.info.total_capacity_Wh; 25 | 26 | datalayer.battery.status.remaining_capacity_Wh; 27 | 28 | datalayer.battery.status.max_discharge_power_W; 29 | 30 | datalayer.battery.status.max_charge_power_W; 31 | 32 | datalayer.battery.status.temperature_min_dC; 33 | 34 | datalayer.battery.status.temperature_max_dC; 35 | } 36 | 37 | void Mg5Battery::handle_incoming_can_frame(CAN_frame rx_frame) { 38 | datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; 39 | switch (rx_frame.ID) { 40 | case 0x171: //Following messages were detected on a MG5 battery BMS 41 | datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN 42 | break; 43 | case 0x172: 44 | break; 45 | case 0x173: 46 | break; 47 | case 0x293: 48 | break; 49 | case 0x295: 50 | break; 51 | case 0x297: 52 | break; 53 | case 0x29B: 54 | break; 55 | case 0x29C: 56 | break; 57 | case 0x2A0: 58 | break; 59 | case 0x2A2: 60 | break; 61 | case 0x322: 62 | break; 63 | case 0x334: 64 | break; 65 | case 0x33F: 66 | break; 67 | case 0x391: 68 | break; 69 | case 0x393: 70 | break; 71 | case 0x3AB: 72 | break; 73 | case 0x3AC: 74 | break; 75 | case 0x3B8: 76 | break; 77 | case 0x3BA: 78 | break; 79 | case 0x3BC: 80 | break; 81 | case 0x3BE: 82 | break; 83 | case 0x3C0: 84 | break; 85 | case 0x3C2: 86 | break; 87 | case 0x400: 88 | break; 89 | case 0x402: 90 | break; 91 | case 0x418: 92 | break; 93 | case 0x44C: 94 | break; 95 | case 0x620: 96 | break; 97 | default: 98 | break; 99 | } 100 | } 101 | void Mg5Battery::transmit_can(unsigned long currentMillis) { 102 | //Send 10ms message 103 | if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { 104 | previousMillis10 = currentMillis; 105 | 106 | transmit_can_frame(&MG_5_100, can_config.battery); 107 | } 108 | // Send 100ms CAN Message 109 | if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { 110 | previousMillis100 = currentMillis; 111 | 112 | //transmit_can_frame(&MG_5_100, can_config.battery); 113 | } 114 | } 115 | 116 | void Mg5Battery::setup(void) { // Performs one time setup at startup 117 | strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63); 118 | datalayer.system.info.battery_protocol[63] = '\0'; 119 | datalayer.system.status.battery_allows_contactor_closing = true; 120 | datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 121 | datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; 122 | datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; 123 | datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /Software/src/battery/MG-5-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef MG_5_BATTERY_H 2 | #define MG_5_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS Mg5Battery 10 | 11 | class Mg5Battery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V 20 | static const int MIN_PACK_VOLTAGE_DV = 3100; 21 | static const int MAX_CELL_DEVIATION_MV = 150; 22 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 24 | 25 | unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send 26 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send 27 | 28 | int BMS_SOC = 0; 29 | 30 | CAN_frame MG_5_100 = {.FD = false, 31 | .ext_ID = false, 32 | .DLC = 8, 33 | .ID = 0x100, 34 | .data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}}; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /Software/src/battery/NISSAN-LEAF-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _NISSAN_LEAF_HTML_H 2 | #define _NISSAN_LEAF_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | String get_status_html() { 11 | String content; 12 | 13 | static const char* LEAFgen[] = {"ZE0", "AZE0", "ZE1"}; 14 | content += "

LEAF generation: " + String(LEAFgen[datalayer_extended.nissanleaf.LEAF_gen]) + "

"; 15 | char readableSerialNumber[16]; // One extra space for null terminator 16 | memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber, 17 | sizeof(datalayer_extended.nissanleaf.BatterySerialNumber)); 18 | readableSerialNumber[15] = '\0'; // Null terminate the string 19 | content += "

Serial number: " + String(readableSerialNumber) + "

"; 20 | char readablePartNumber[8]; // One extra space for null terminator 21 | memcpy(readablePartNumber, datalayer_extended.nissanleaf.BatteryPartNumber, 22 | sizeof(datalayer_extended.nissanleaf.BatteryPartNumber)); 23 | readablePartNumber[7] = '\0'; // Null terminate the string 24 | content += "

Part number: " + String(readablePartNumber) + "

"; 25 | char readableBMSID[9]; // One extra space for null terminator 26 | memcpy(readableBMSID, datalayer_extended.nissanleaf.BMSIDcode, sizeof(datalayer_extended.nissanleaf.BMSIDcode)); 27 | readableBMSID[8] = '\0'; // Null terminate the string 28 | content += "

BMS ID: " + String(readableBMSID) + "

"; 29 | content += "

GIDS: " + String(datalayer_extended.nissanleaf.GIDS) + "

"; 30 | content += "

Regen kW: " + String(datalayer_extended.nissanleaf.ChargePowerLimit) + "

"; 31 | content += "

Charge kW: " + String(datalayer_extended.nissanleaf.MaxPowerForCharger) + "

"; 32 | content += "

Interlock: " + String(datalayer_extended.nissanleaf.Interlock) + "

"; 33 | content += "

Insulation: " + String(datalayer_extended.nissanleaf.Insulation) + "

"; 34 | content += "

Relay cut request: " + String(datalayer_extended.nissanleaf.RelayCutRequest) + "

"; 35 | content += "

Failsafe status: " + String(datalayer_extended.nissanleaf.FailsafeStatus) + "

"; 36 | content += "

Fully charged: " + String(datalayer_extended.nissanleaf.Full) + "

"; 37 | content += "

Battery empty: " + String(datalayer_extended.nissanleaf.Empty) + "

"; 38 | content += "

Main relay ON: " + String(datalayer_extended.nissanleaf.MainRelayOn) + "

"; 39 | content += "

Heater present: " + String(datalayer_extended.nissanleaf.HeatExist) + "

"; 40 | content += "

Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "

"; 41 | content += "

Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "

"; 42 | content += "

Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "

"; 43 | content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; 44 | content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + 45 | String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "

"; 46 | content += "

Challenge failed: " + String(datalayer_extended.nissanleaf.challengeFailed) + "

"; 47 | 48 | return content; 49 | } 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /Software/src/battery/ORION-BMS.h: -------------------------------------------------------------------------------- 1 | #ifndef ORION_BMS_H 2 | #define ORION_BMS_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS OrionBms 10 | 11 | class OrionBms : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | /* Change the following to suit your battery */ 20 | static const int NUMBER_OF_CELLS = 96; 21 | static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V 22 | static const int MIN_PACK_VOLTAGE_DV = 1500; 23 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 24 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 25 | static const int MAX_CELL_DEVIATION_MV = 150; 26 | 27 | uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages 28 | uint16_t Maximum_Cell_Voltage = 3700; 29 | uint16_t Minimum_Cell_Voltage = 3700; 30 | uint16_t Pack_Health = 99; 31 | int16_t Pack_Current = 0; 32 | int16_t Average_Temperature = 0; 33 | uint16_t Pack_Summed_Voltage = 0; 34 | int16_t Average_Current = 0; 35 | uint16_t High_Temperature = 0; 36 | uint16_t Pack_SOC_ppt = 0; 37 | uint16_t Pack_CCL = 0; //Charge current limit (A) 38 | uint16_t Pack_DCL = 0; //Discharge current limit (A) 39 | uint16_t Maximum_Pack_Voltage = 0; 40 | uint16_t Minimum_Pack_Voltage = 0; 41 | uint16_t CellID = 0; 42 | uint16_t CellVoltage = 0; 43 | uint16_t CellResistance = 0; 44 | uint16_t CellOpenVoltage = 0; 45 | uint16_t Checksum = 0; 46 | uint16_t CellBalancing = 0; 47 | uint8_t amount_of_detected_cells = 0; 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /Software/src/battery/RENAULT-KANGOO-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef RENAULT_KANGOO_BATTERY_H 2 | #define RENAULT_KANGOO_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS RenaultKangooBattery 10 | 11 | class RenaultKangooBattery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V 20 | static const int MIN_PACK_VOLTAGE_DV = 2500; 21 | static const int MAX_CELL_DEVIATION_MV = 150; 22 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 24 | static const int MAX_CHARGE_POWER_W = 5000; // Battery can be charged with this amount of power 25 | 26 | uint32_t LB_Battery_Voltage = 3700; 27 | uint32_t LB_Charge_Power_Limit_Watts = 0; 28 | int32_t LB_Current = 0; 29 | int16_t LB_MAX_TEMPERATURE = 0; 30 | int16_t LB_MIN_TEMPERATURE = 0; 31 | uint16_t LB_SOC = 0; 32 | uint16_t LB_SOH = 0; 33 | uint16_t LB_Discharge_Power_Limit = 0; 34 | uint16_t LB_Charge_Power_Limit = 0; 35 | uint16_t LB_kWh_Remaining = 0; 36 | uint16_t LB_Cell_Max_Voltage = 3700; 37 | uint16_t LB_Cell_Min_Voltage = 3700; 38 | uint16_t LB_MaxChargeAllowed_W = 0; 39 | uint8_t LB_Discharge_Power_Limit_Byte1 = 0; 40 | uint8_t GVI_Pollcounter = 0; 41 | uint8_t LB_EOCR = 0; 42 | uint8_t LB_HVBUV = 0; 43 | uint8_t LB_HVBIR = 0; 44 | uint8_t LB_CUV = 0; 45 | uint8_t LB_COV = 0; 46 | uint8_t LB_HVBOV = 0; 47 | uint8_t LB_HVBOT = 0; 48 | uint8_t LB_HVBOC = 0; 49 | uint8_t LB_MaxInput_kW = 0; 50 | uint8_t LB_MaxOutput_kW = 0; 51 | bool GVB_79B_Continue = false; 52 | 53 | CAN_frame KANGOO_423 = {.FD = false, 54 | .ext_ID = false, 55 | .DLC = 8, 56 | .ID = 0x423, 57 | .data = {0x0B, 0x1D, 0x00, 0x02, 0xB2, 0x20, 0xB2, 0xD9}}; // Charging 58 | // Driving: 0x07 0x1D 0x00 0x02 0x5D 0x80 0x5D 0xD8 59 | // Charging: 0x0B 0x1D 0x00 0x02 0xB2 0x20 0xB2 0xD9 60 | // Fastcharging: 0x07 0x1E 0x00 0x01 0x5D 0x20 0xB2 0xC7 61 | // Old hardcoded message: .data = {0x33, 0x00, 0xFF, 0xFF, 0x00, 0xE0, 0x00, 0x00}}; 62 | CAN_frame KANGOO_79B = {.FD = false, 63 | .ext_ID = false, 64 | .DLC = 8, 65 | .ID = 0x79B, 66 | .data = {0x02, 0x21, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00}}; 67 | CAN_frame KANGOO_79B_Continue = {.FD = false, 68 | .ext_ID = false, 69 | .DLC = 8, 70 | .ID = 0x79B, 71 | .data = {0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 72 | 73 | unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent 74 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent 75 | unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent 76 | unsigned long GVL_pause = 0; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Software/src/battery/RENAULT-TWIZY.h: -------------------------------------------------------------------------------- 1 | #ifndef RENAULT_TWIZY_BATTERY_H 2 | #define RENAULT_TWIZY_BATTERY_H 3 | #include "../include.h" 4 | #include "CanBattery.h" 5 | 6 | #define BATTERY_SELECTED 7 | #define SELECTED_BATTERY_CLASS RenaultTwizyBattery 8 | 9 | class RenaultTwizyBattery : public CanBattery { 10 | public: 11 | virtual void setup(void); 12 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 13 | virtual void update_values(); 14 | virtual void transmit_can(unsigned long currentMillis); 15 | 16 | private: 17 | static const int MAX_PACK_VOLTAGE_DV = 579; // 57.9V at 100% SOC (with 70% SOH, new one might be higher) 18 | static const int MIN_PACK_VOLTAGE_DV = 480; // 48.4V at 13.76% SOC 19 | static const int MAX_CELL_DEVIATION_MV = 150; 20 | static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value 21 | static const int MIN_CELL_VOLTAGE_MV = 3400; //Battery is put into emergency stop if one cell goes below this value 22 | 23 | int16_t cell_temperatures_dC[7] = {0}; 24 | int16_t current_dA = 0; 25 | uint16_t voltage_dV = 0; 26 | int16_t cellvoltages_mV[14] = {0}; 27 | int16_t max_discharge_power = 0; 28 | int16_t max_recup_power = 0; 29 | int16_t max_charge_power = 0; 30 | uint16_t SOC = 0; 31 | uint16_t SOH = 0; 32 | uint16_t remaining_capacity_Wh = 0; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef RENAULT_ZOE_GEN1_BATTERY_H 2 | #define RENAULT_ZOE_GEN1_BATTERY_H 3 | 4 | #include "CanBattery.h" 5 | #include "RENAULT-ZOE-GEN1-HTML.h" 6 | 7 | #define BATTERY_SELECTED 8 | #define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery 9 | 10 | #define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V 11 | #define MIN_PACK_VOLTAGE_DV 3000 12 | #define MAX_CELL_DEVIATION_MV 150 13 | #define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value 14 | #define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value 15 | 16 | class RenaultZoeGen1Battery : public CanBattery { 17 | public: 18 | virtual void setup(void); 19 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 20 | virtual void update_values(); 21 | virtual void transmit_can(unsigned long currentMillis); 22 | 23 | BatteryHtmlRenderer& get_status_renderer() { return renderer; } 24 | 25 | private: 26 | RenaultZoeGen1HtmlRenderer renderer; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Software/src/battery/RENAULT-ZOE-GEN1-HTML.h: -------------------------------------------------------------------------------- 1 | #ifndef _RENAULT_ZOE_GEN1_HTML_H 2 | #define _RENAULT_ZOE_GEN1_HTML_H 3 | 4 | #include "../datalayer/datalayer.h" 5 | #include "../datalayer/datalayer_extended.h" 6 | #include "src/devboard/webserver/BatteryHtmlRenderer.h" 7 | 8 | class RenaultZoeGen1HtmlRenderer : public BatteryHtmlRenderer { 9 | public: 10 | String get_status_html() { 11 | String content; 12 | 13 | content += "

CUV " + String(datalayer_extended.zoe.CUV) + "

"; 14 | content += "

HVBIR " + String(datalayer_extended.zoe.HVBIR) + "

"; 15 | content += "

HVBUV " + String(datalayer_extended.zoe.HVBUV) + "

"; 16 | content += "

EOCR " + String(datalayer_extended.zoe.EOCR) + "

"; 17 | content += "

HVBOC " + String(datalayer_extended.zoe.HVBOC) + "

"; 18 | content += "

HVBOT " + String(datalayer_extended.zoe.HVBOT) + "

"; 19 | content += "

HVBOV " + String(datalayer_extended.zoe.HVBOV) + "

"; 20 | content += "

COV " + String(datalayer_extended.zoe.COV) + "

"; 21 | 22 | return content; 23 | } 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Software/src/battery/RS485Battery.h: -------------------------------------------------------------------------------- 1 | #ifndef RS485_BATTERY_H 2 | #define RS485_BATTERY_H 3 | 4 | #include "Battery.h" 5 | 6 | #include "src/devboard/utils/types.h" 7 | 8 | // Abstract base class for batteries using the RS485 interface 9 | class RS485Battery : public Battery { 10 | public: 11 | virtual void receive_RS485() = 0; 12 | virtual void transmit_rs485(unsigned long currentMillis) = 0; 13 | String interface_name() { return "RS485"; } 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Software/src/battery/SIMPBMS-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPBMS_BATTERY_H 2 | #define SIMPBMS_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS SimpBmsBattery 10 | 11 | class SimpBmsBattery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | /* DEFAULT VALUES BMS will send configured */ 20 | static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V 21 | static const int MIN_PACK_VOLTAGE_DV = 1500; 22 | static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 24 | static const int MAX_CELL_DEVIATION_MV = 500; 25 | static const int CELL_COUNT = 96; 26 | 27 | static const int SIMPBMS_MAX_CELLS = 128; 28 | 29 | unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent 30 | 31 | //Actual content messages 32 | 33 | int16_t celltemperature_max_dC = 0; 34 | int16_t celltemperature_min_dC = 0; 35 | int16_t current_dA = 0; 36 | uint16_t voltage_dV = 0; 37 | uint16_t cellvoltage_max_mV = 3700; 38 | uint16_t cellvoltage_min_mV = 3700; 39 | uint16_t charge_cutoff_voltage = 0; 40 | uint16_t discharge_cutoff_voltage = 0; 41 | int16_t max_charge_current = 0; 42 | int16_t max_discharge_current = 0; 43 | uint8_t ensemble_info_ack = 0; 44 | uint8_t cells_in_series = 0; 45 | uint8_t voltage_level = 0; 46 | uint8_t ah_total = 0; 47 | uint8_t SOC = 0; 48 | uint8_t SOH = 99; 49 | uint8_t charge_forbidden = 0; 50 | uint8_t discharge_forbidden = 0; 51 | uint16_t cellvoltages_mV[SIMPBMS_MAX_CELLS] = {0}; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Software/src/battery/SONO-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef SONO_BATTERY_H 2 | #define SONO_BATTERY_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanBattery.h" 7 | 8 | #define BATTERY_SELECTED 9 | #define SELECTED_BATTERY_CLASS SonoBattery 10 | 11 | class SonoBattery : public CanBattery { 12 | public: 13 | virtual void setup(void); 14 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 15 | virtual void update_values(); 16 | virtual void transmit_can(unsigned long currentMillis); 17 | 18 | private: 19 | static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V 20 | static const int MIN_PACK_VOLTAGE_DV = 2500; 21 | static const int MAX_CELL_DEVIATION_MV = 250; 22 | static const int MAX_CELL_VOLTAGE_MV = 3800; //Battery is put into emergency stop if one cell goes over this value 23 | static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value 24 | 25 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send 26 | unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send 27 | 28 | uint8_t seconds = 0; 29 | uint8_t functionalsafetybitmask = 0; 30 | uint16_t batteryVoltage = 3700; 31 | uint16_t allowedDischargePower = 0; 32 | uint16_t allowedChargePower = 0; 33 | uint16_t CellVoltMax_mV = 0; 34 | uint16_t CellVoltMin_mV = 0; 35 | int16_t batteryAmps = 0; 36 | int16_t temperatureMin = 0; 37 | int16_t temperatureMax = 0; 38 | uint8_t batterySOH = 99; 39 | uint8_t realSOC = 99; 40 | 41 | CAN_frame SONO_400 = {.FD = false, //Message of Vehicle Command, 100ms 42 | .ext_ID = false, 43 | .DLC = 8, 44 | .ID = 0x400, 45 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 46 | CAN_frame SONO_401 = {.FD = false, //Message of Vehicle Date, 1000ms 47 | .ext_ID = false, 48 | .DLC = 8, 49 | .ID = 0x400, 50 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Software/src/battery/TEST-FAKE-BATTERY.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_FAKE_BATTERY_H 2 | #define TEST_FAKE_BATTERY_H 3 | #include "../datalayer/datalayer.h" 4 | #include "../include.h" 5 | #include "CanBattery.h" 6 | 7 | #define BATTERY_SELECTED 8 | #define SELECTED_BATTERY_CLASS TestFakeBattery 9 | 10 | class TestFakeBattery : public CanBattery { 11 | public: 12 | // Use this constructor for the second battery. 13 | TestFakeBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, CAN_Interface targetCan) : CanBattery(targetCan) { 14 | datalayer_battery = datalayer_ptr; 15 | allows_contactor_closing = nullptr; 16 | } 17 | 18 | // Use the default constructor to create the first or single battery. 19 | TestFakeBattery() { 20 | datalayer_battery = &datalayer.battery; 21 | allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; 22 | } 23 | 24 | virtual void setup(); 25 | virtual void handle_incoming_can_frame(CAN_frame rx_frame); 26 | virtual void update_values(); 27 | virtual void transmit_can(unsigned long currentMillis); 28 | 29 | bool supports_set_fake_voltage() { return true; } 30 | void set_fake_voltage(float val) { datalayer.battery.status.voltage_dV = val * 10; } 31 | 32 | private: 33 | DATALAYER_BATTERY_TYPE* datalayer_battery; 34 | // If not null, this battery decides when the contactor can be closed and writes the value here. 35 | bool* allows_contactor_closing; 36 | 37 | static const int MAX_CELL_DEVIATION_MV = 9999; 38 | 39 | unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send 40 | unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send 41 | unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send 42 | 43 | CAN_frame TEST = {.FD = false, 44 | .ext_ID = false, 45 | .DLC = 8, 46 | .ID = 0x123, 47 | .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /Software/src/charger/CHARGERS.cpp: -------------------------------------------------------------------------------- 1 | #include "../include.h" 2 | 3 | CanCharger* charger = nullptr; 4 | 5 | void setup_charger() { 6 | #ifdef SELECTED_CHARGER_CLASS 7 | charger = new SELECTED_CHARGER_CLASS(); 8 | #endif 9 | } 10 | -------------------------------------------------------------------------------- /Software/src/charger/CHARGERS.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARGERS_H 2 | #define CHARGERS_H 3 | #include "../../USER_SETTINGS.h" 4 | 5 | #include "CHEVY-VOLT-CHARGER.h" 6 | #include "NISSAN-LEAF-CHARGER.h" 7 | 8 | // Constructs the global charger object based on build-time selection of charger type. 9 | // Safe to call even though no charger is selected. 10 | void setup_charger(); 11 | 12 | // The selected charger or null if no charger in use. 13 | extern CanCharger* charger; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Software/src/charger/CHEVY-VOLT-CHARGER.h: -------------------------------------------------------------------------------- 1 | #ifndef CHEVYVOLT_CHARGER_H 2 | #define CHEVYVOLT_CHARGER_H 3 | #include 4 | #include "../datalayer/datalayer.h" 5 | #include "../include.h" 6 | #include "CanCharger.h" 7 | 8 | #ifdef CHEVYVOLT_CHARGER 9 | #define SELECTED_CHARGER_CLASS ChevyVoltCharger 10 | #endif 11 | 12 | class ChevyVoltCharger : public CanCharger { 13 | public: 14 | ChevyVoltCharger() : CanCharger(ChargerType::ChevyVolt) {} 15 | 16 | const char* name() { return "Chevy Volt Gen1 Charger"; } 17 | 18 | void map_can_frame_to_variable(CAN_frame rx_frame); 19 | void transmit_can(unsigned long currentMillis); 20 | 21 | float outputPowerDC() { 22 | return static_cast(datalayer.charger.charger_stat_HVcur * datalayer.charger.charger_stat_HVvol); 23 | } 24 | bool efficiencySupported() { return true; } 25 | float efficiency() { 26 | float chgPwrAC = static_cast(datalayer.charger.charger_stat_ACcur * datalayer.charger.charger_stat_ACvol); 27 | return outputPowerDC() / chgPwrAC * 100; 28 | } 29 | 30 | private: 31 | /* Charger hardware limits 32 | * 33 | * Relative to runtime settings, expectations are: 34 | * hw minimum <= setting minimum <= setting maximum <= hw max 35 | */ 36 | const float CHEVYVOLT_MAX_HVDC = 420.0; 37 | const float CHEVYVOLT_MIN_HVDC = 200.0; 38 | const float CHEVYVOLT_MAX_AMP = 11.5; 39 | const float CHEVYVOLT_MAX_POWER = 3300; 40 | 41 | /* CAN cycles and timers */ 42 | unsigned long previousMillis30ms = 0; // 30ms cycle for keepalive frames 43 | unsigned long previousMillis200ms = 0; // 200ms cycle for commanding I/V targets 44 | unsigned long previousMillis5000ms = 0; // 5s status printout to serial 45 | 46 | enum CHARGER_MODES : uint8_t { MODE_DISABLED = 0, MODE_LV, MODE_HV, MODE_HVLV }; 47 | 48 | //Actual content messages 49 | CAN_frame charger_keepalive_frame = {.FD = false, 50 | .ext_ID = false, 51 | .DLC = 1, 52 | .ID = 0x30E, //one byte only, indicating enabled or disabled 53 | .data = {MODE_DISABLED}}; 54 | 55 | CAN_frame charger_set_targets = {.FD = false, 56 | .ext_ID = false, 57 | .DLC = 4, 58 | .ID = 0x304, 59 | .data = {0x40, 0x00, 0x00, 0x00}}; // data[0] is a value, meaning unknown 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Software/src/charger/CanCharger.h: -------------------------------------------------------------------------------- 1 | #ifndef CAN_CHARGER_H 2 | #define CAN_CHARGER_H 3 | 4 | #include "src/devboard/utils/types.h" 5 | 6 | #include "../datalayer/datalayer.h" 7 | 8 | enum class ChargerType { NissanLeaf, ChevyVolt }; 9 | 10 | // Generic base class for all chargers 11 | class Charger { 12 | public: 13 | ChargerType type() { return m_type; } 14 | 15 | virtual const char* name() = 0; 16 | 17 | virtual float outputPowerDC() = 0; 18 | 19 | virtual float HVDC_output_voltage() { return datalayer.charger.charger_stat_HVvol; } 20 | virtual float HVDC_output_current() { return datalayer.charger.charger_stat_HVcur; } 21 | 22 | virtual float LVDC_output_voltage() { return datalayer.charger.charger_stat_LVvol; } 23 | virtual float LVDC_output_current() { return datalayer.charger.charger_stat_LVcur; } 24 | 25 | virtual float AC_input_voltage() { return datalayer.charger.charger_stat_ACvol; } 26 | virtual float AC_input_current() { return datalayer.charger.charger_stat_ACcur; } 27 | 28 | virtual bool efficiencySupported() { return false; } 29 | virtual float efficiency() { return 0; } 30 | 31 | protected: 32 | Charger(ChargerType type) : m_type(type) {} 33 | 34 | private: 35 | ChargerType m_type; 36 | }; 37 | 38 | // Base class for chargers on a CAN bus 39 | class CanCharger : public Charger { 40 | public: 41 | virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; 42 | virtual void transmit_can(unsigned long currentMillis) = 0; 43 | 44 | protected: 45 | CanCharger(ChargerType type) : Charger(type) {} 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /Software/src/charger/NISSAN-LEAF-CHARGER.h: -------------------------------------------------------------------------------- 1 | #ifndef NISSANLEAF_CHARGER_H 2 | #define NISSANLEAF_CHARGER_H 3 | #include 4 | #include "../include.h" 5 | 6 | #include "CanCharger.h" 7 | 8 | #ifdef NISSANLEAF_CHARGER 9 | #define SELECTED_CHARGER_CLASS NissanLeafCharger 10 | #endif 11 | 12 | class NissanLeafCharger : public CanCharger { 13 | public: 14 | NissanLeafCharger() : CanCharger(ChargerType::NissanLeaf) {} 15 | 16 | const char* name() { return "Nissan LEAF 2013-2024 PDM charger"; } 17 | void map_can_frame_to_variable(CAN_frame rx_frame); 18 | void transmit_can(unsigned long currentMillis); 19 | 20 | float outputPowerDC() { return static_cast(datalayer.charger.charger_stat_HVcur * 100); } 21 | 22 | float HVDC_output_current() { 23 | // P/U=I 24 | if (datalayer.battery.status.voltage_dV > 0) { 25 | return outputPowerDC() / (datalayer.battery.status.voltage_dV / 10); 26 | } 27 | return 0; 28 | } 29 | 30 | float HVDC_output_voltage() { return static_cast(datalayer.battery.status.voltage_dV / 10); } 31 | 32 | private: 33 | /* CAN cycles and timers */ 34 | unsigned long previousMillis10ms = 0; 35 | unsigned long previousMillis100ms = 0; 36 | 37 | /* LEAF charger/battery parameters */ 38 | enum OBC_MODES : uint8_t { 39 | IDLE_OR_QC = 1, 40 | FINISHED = 2, 41 | CHARGING_OR_INTERRUPTED = 4, 42 | IDLE1 = 8, 43 | IDLE2 = 9, 44 | PLUGGED_IN_WAITING_ON_TIMER 45 | }; 46 | enum OBC_VOLTAGES : uint8_t { NO_SIGNAL = 0, AC110 = 1, AC230 = 2, ABNORMAL_WAVE = 3 }; 47 | uint16_t OBC_Charge_Power = 0; // Actual charger output 48 | uint8_t mprun100 = 0; // Counter 0-3 49 | uint8_t mprun10 = 0; // Counter 0-3 50 | uint8_t OBC_Charge_Status = IDLE_OR_QC; 51 | uint8_t OBC_Status_AC_Voltage = 0; //1=110V, 2=230V 52 | uint8_t OBCpowerSetpoint = 0; 53 | uint8_t OBCpower = 0; 54 | bool PPStatus = false; 55 | bool OBCwakeup = false; 56 | 57 | //Actual content messages 58 | CAN_frame LEAF_1DB = {.FD = false, 59 | .ext_ID = false, 60 | .DLC = 8, 61 | .ID = 0x1DB, 62 | .data = {0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00}}; 63 | CAN_frame LEAF_1DC = {.FD = false, 64 | .ext_ID = false, 65 | .DLC = 8, 66 | .ID = 0x1DC, 67 | .data = {0x6E, 0x0A, 0x05, 0xD5, 0x00, 0x00, 0x00, 0x00}}; 68 | CAN_frame LEAF_1F2 = {.FD = false, 69 | .ext_ID = false, 70 | .DLC = 8, 71 | .ID = 0x1F2, 72 | .data = {0x30, 0x00, 0x20, 0xAC, 0x00, 0x3C, 0x00, 0x8F}}; 73 | CAN_frame LEAF_50B = {.FD = false, 74 | .ext_ID = false, 75 | .DLC = 7, 76 | .ID = 0x50B, 77 | .data = {0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x00}}; 78 | CAN_frame LEAF_55B = {.FD = false, 79 | .ext_ID = false, 80 | .DLC = 8, 81 | .ID = 0x55B, 82 | .data = {0xA4, 0x40, 0xAA, 0x00, 0xDF, 0xC0, 0x10, 0x00}}; 83 | CAN_frame LEAF_5BC = {.FD = false, 84 | .ext_ID = false, 85 | .DLC = 8, 86 | .ID = 0x5BC, 87 | .data = {0x3D, 0x80, 0xF0, 0x64, 0xB0, 0x01, 0x00, 0x32}}; 88 | 89 | CAN_frame LEAF_59E = {.FD = false, 90 | .ext_ID = false, 91 | .DLC = 8, 92 | .ID = 0x59E, 93 | .data = {0x00, 0x00, 0x0C, 0x76, 0x18, 0x00, 0x00, 0x00}}; 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /Software/src/communication/can/comm_can.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMM_CAN_H_ 2 | #define _COMM_CAN_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #include "../../datalayer/datalayer.h" 7 | #include "../../devboard/utils/events.h" 8 | #include "../../devboard/utils/value_mapping.h" 9 | #include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h" 10 | #include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" 11 | #ifdef CAN_ADDON 12 | #include "../../lib/pierremolinaro-acan2515/ACAN2515.h" 13 | #endif //CAN_ADDON 14 | #ifdef CANFD_ADDON 15 | #include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" 16 | #endif //CANFD_ADDON 17 | 18 | void dump_can_frame(CAN_frame& frame, frameDirection msgDir); 19 | void transmit_can_frame(CAN_frame* tx_frame, int interface); 20 | 21 | /** 22 | * @brief Initialization function for CAN. 23 | * 24 | * @param[in] void 25 | * 26 | * @return void 27 | */ 28 | void init_CAN(); 29 | 30 | /** 31 | * @brief Transmit one CAN frame 32 | * 33 | * @param[in] CAN_frame* tx_frame 34 | * @param[in] int interface 35 | * 36 | * @return void 37 | */ 38 | void transmit_can_frame(); 39 | 40 | /** 41 | * @brief Send CAN messages to all components 42 | * 43 | * @param[in] void 44 | * @param[in] unsigned long currentMillis 45 | * 46 | * @return void 47 | */ 48 | void transmit_can(unsigned long currentMillis); 49 | 50 | /** 51 | * @brief Receive CAN messages from all interfaces 52 | * 53 | * @param[in] void 54 | * 55 | * @return void 56 | */ 57 | void receive_can(); 58 | 59 | /** 60 | * @brief Receive CAN messages from CAN tranceiver natively installed on Lilygo hardware 61 | * 62 | * @param[in] void 63 | * 64 | * @return void 65 | */ 66 | void receive_frame_can_native(); 67 | 68 | /** 69 | * @brief Receive CAN messages from CAN addon chip 70 | * 71 | * @param[in] void 72 | * 73 | * @return void 74 | */ 75 | void receive_frame_can_addon(); 76 | 77 | /** 78 | * @brief Receive CAN messages from CANFD addon chip 79 | * 80 | * @param[in] void 81 | * 82 | * @return void 83 | */ 84 | void receive_frame_canfd_addon(); 85 | 86 | /** 87 | * @brief print CAN frames via USB 88 | * 89 | * @param[in] void 90 | * 91 | * @return void 92 | */ 93 | void print_can_frame(CAN_frame frame, frameDirection msgDir); 94 | 95 | /** 96 | * @brief Map CAN frame from specified interface to variable 97 | * 98 | * @param[in] CAN_frame* rx_frame 99 | * @param[in] int interface 100 | * 101 | * @return void 102 | */ 103 | void map_can_frame_to_variable(CAN_frame* rx_frame, int interface); 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /Software/src/communication/can/obd.h: -------------------------------------------------------------------------------- 1 | #ifndef _OBD_H_ 2 | #define _OBD_H_ 3 | 4 | #include "../../include.h" 5 | #include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" 6 | 7 | void handle_obd_frame(CAN_frame& rx_frame); 8 | 9 | void transmit_obd_can_frame(unsigned int address, int interface, bool canFD); 10 | 11 | #endif // _OBD_H_ 12 | -------------------------------------------------------------------------------- /Software/src/communication/contactorcontrol/comm_contactorcontrol.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMM_CONTACTORCONTROL_H_ 2 | #define _COMM_CONTACTORCONTROL_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #include "../../datalayer/datalayer.h" 7 | #include "../../devboard/utils/events.h" 8 | 9 | /** 10 | * @brief Handle BMS power output 11 | * 12 | * @param[in] void 13 | * 14 | * @return void 15 | */ 16 | void handle_BMSpower(); 17 | 18 | /** 19 | * @brief Start BMS reset sequence 20 | * 21 | * @param[in] void 22 | * 23 | * @return void 24 | */ 25 | void start_bms_reset(); 26 | 27 | /** 28 | * @brief Contactor initialization 29 | * 30 | * @param[in] void 31 | * 32 | * @return void 33 | */ 34 | void init_contactors(); 35 | 36 | /** 37 | * @brief Handle contactors 38 | * 39 | * @param[in] void 40 | * 41 | * @return void 42 | */ 43 | void handle_contactors(); 44 | 45 | /** 46 | * @brief Handle contactors of battery 2 47 | * 48 | * @param[in] void 49 | * 50 | * @return void 51 | */ 52 | void handle_contactors_battery2(); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp: -------------------------------------------------------------------------------- 1 | #include "comm_equipmentstopbutton.h" 2 | #include "../../include.h" 3 | 4 | // Parameters 5 | #ifdef EQUIPMENT_STOP_BUTTON 6 | const unsigned long equipment_button_long_press_duration = 7 | 15000; // 15 seconds for long press in case of MOMENTARY_SWITCH 8 | const unsigned long equipment_button_debounce_duration = 200; // 200ms for debouncing the button 9 | unsigned long timeSincePress = 0; // Variable to store the time since the last press 10 | DebouncedButton equipment_stop_button; // Debounced button object 11 | #endif // EQUIPMENT_STOP_BUTTON 12 | 13 | // Initialization functions 14 | #ifdef EQUIPMENT_STOP_BUTTON 15 | void init_equipment_stop_button() { 16 | //using external pullup resistors NC 17 | pinMode(EQUIPMENT_STOP_PIN, INPUT); 18 | // Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time 19 | initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration); 20 | } 21 | #endif // EQUIPMENT_STOP_BUTTON 22 | 23 | // Main functions 24 | 25 | #ifdef EQUIPMENT_STOP_BUTTON 26 | void monitor_equipment_stop_button() { 27 | 28 | ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress); 29 | 30 | if (equipment_stop_behavior == LATCHING_SWITCH) { 31 | if (changed_state == PRESSED) { 32 | // Changed to ON – initiating equipment stop. 33 | setBatteryPause(true, false, true); 34 | } else if (changed_state == RELEASED) { 35 | // Changed to OFF – ending equipment stop. 36 | setBatteryPause(false, false, false); 37 | } 38 | } else if (equipment_stop_behavior == MOMENTARY_SWITCH) { 39 | if (changed_state == RELEASED) { // button is released 40 | 41 | if (timeSincePress < equipment_button_long_press_duration) { 42 | // Short press detected, trigger equipment stop 43 | setBatteryPause(true, false, true); 44 | } else { 45 | // Long press detected, reset equipment stop state 46 | setBatteryPause(false, false, false); 47 | } 48 | } 49 | } 50 | } 51 | #endif // EQUIPMENT_STOP_BUTTON 52 | -------------------------------------------------------------------------------- /Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMM_EQUIPMENTSTOPBUTTON_H_ 2 | #define _COMM_EQUIPMENTSTOPBUTTON_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #ifdef EQUIPMENT_STOP_BUTTON 7 | #include "../../devboard/utils/debounce_button.h" 8 | #endif 9 | 10 | /** 11 | * @brief Initialization of equipment stop button 12 | * 13 | * @param[in] void 14 | * 15 | * @return void 16 | */ 17 | void init_equipment_stop_button(); 18 | 19 | /** 20 | * @brief Monitor equipment stop button 21 | * 22 | * @param[in] void 23 | * 24 | * @return void 25 | */ 26 | void monitor_equipment_stop_button(); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Software/src/communication/nvm/comm_nvm.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMM_NVM_H_ 2 | #define _COMM_NVM_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #include "../../datalayer/datalayer.h" 7 | #include "../../devboard/utils/events.h" 8 | #include "../../devboard/wifi/wifi.h" 9 | 10 | /** 11 | * @brief Initialization of setting storage 12 | * 13 | * @param[in] void 14 | * 15 | * @return void 16 | */ 17 | void init_stored_settings(); 18 | 19 | /** 20 | * @brief Store settings of equipment stop button 21 | * 22 | * @param[in] void 23 | * 24 | * @return void 25 | */ 26 | void store_settings_equipment_stop(); 27 | 28 | /** 29 | * @brief Store settings 30 | * 31 | * @param[in] void 32 | * 33 | * @return void 34 | */ 35 | void store_settings(); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /Software/src/communication/precharge_control/precharge_control.h: -------------------------------------------------------------------------------- 1 | #ifndef _PRECHARGE_CONTROL_H_ 2 | #define _PRECHARGE_CONTROL_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #include "../../devboard/utils/events.h" 7 | 8 | /** 9 | * @brief Contactor initialization 10 | * 11 | * @param[in] void 12 | * 13 | * @return void 14 | */ 15 | void init_precharge_control(); 16 | 17 | /** 18 | * @brief Handle contactors 19 | * 20 | * @param[in] unsigned long currentMillis 21 | * 22 | * @return void 23 | */ 24 | void handle_precharge_control(unsigned long currentMillis); 25 | 26 | #endif // _PRECHARGE_CONTROL_H_ 27 | -------------------------------------------------------------------------------- /Software/src/communication/rs485/comm_rs485.cpp: -------------------------------------------------------------------------------- 1 | #include "comm_rs485.h" 2 | #include "../../include.h" 3 | 4 | void init_rs485() { 5 | #ifdef RS485_EN_PIN 6 | pinMode(RS485_EN_PIN, OUTPUT); 7 | digitalWrite(RS485_EN_PIN, HIGH); 8 | #endif // RS485_EN_PIN 9 | #ifdef RS485_SE_PIN 10 | pinMode(RS485_SE_PIN, OUTPUT); 11 | digitalWrite(RS485_SE_PIN, HIGH); 12 | #endif // RS485_SE_PIN 13 | #ifdef PIN_5V_EN 14 | pinMode(PIN_5V_EN, OUTPUT); 15 | digitalWrite(PIN_5V_EN, HIGH); 16 | #endif // PIN_5V_EN 17 | 18 | // Inverters and batteries are expected to initialize their serial port in their setup-function 19 | // for RS485 or Modbus comms. 20 | } 21 | -------------------------------------------------------------------------------- /Software/src/communication/rs485/comm_rs485.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMM_RS485_H_ 2 | #define _COMM_RS485_H_ 3 | 4 | #include "../../include.h" 5 | 6 | #include "../../lib/eModbus-eModbus/Logging.h" 7 | #include "../../lib/eModbus-eModbus/ModbusServerRTU.h" 8 | #include "../../lib/eModbus-eModbus/scripts/mbServerFCs.h" 9 | 10 | /** 11 | * @brief Initialization of RS485 12 | * 13 | * @param[in] void 14 | * 15 | * @return void 16 | */ 17 | void init_rs485(); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /Software/src/datalayer/datalayer.cpp: -------------------------------------------------------------------------------- 1 | #include "datalayer.h" 2 | #include "../include.h" 3 | 4 | DataLayer datalayer; 5 | -------------------------------------------------------------------------------- /Software/src/datalayer/datalayer_extended.cpp: -------------------------------------------------------------------------------- 1 | #include "datalayer_extended.h" 2 | #include "../include.h" 3 | 4 | DataLayerExtended datalayer_extended; 5 | -------------------------------------------------------------------------------- /Software/src/devboard/debug/debug.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # 3 | # Example OpenOCD configuration file for ESP32-WROVER-KIT board. 4 | # 5 | # For example, OpenOCD can be started for ESP32 debugging on 6 | # 7 | # openocd -f board/esp32-wrover-kit-3.3v.cfg 8 | # 9 | 10 | # Source the JTAG interface configuration file 11 | source [find interface/ftdi/esp32_devkitj_v1.cfg] 12 | set ESP32_FLASH_VOLTAGE 3.3 13 | # Source the ESP32 configuration file 14 | source [find target/esp32.cfg] 15 | -------------------------------------------------------------------------------- /Software/src/devboard/debug/debug_custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Arduino on ESP32", 3 | "toolchainPrefix":"xtensa-esp32-elf", 4 | "svdFile":"esp32.svd", 5 | "request":"attach", 6 | "postAttachCommands":[ 7 | "set remote hardware-watchpoint-limit 2", 8 | "monitor reset halt", 9 | "monitor gdb_sync", 10 | "thb setup", 11 | "c" 12 | ], 13 | "overrideRestartCommands":[ 14 | "monitor reset halt", 15 | "monitor gdb_sync", 16 | "thb setup", 17 | "c" 18 | ] 19 | } -------------------------------------------------------------------------------- /Software/src/devboard/hal/hal.h: -------------------------------------------------------------------------------- 1 | #ifndef _HAL_H_ 2 | #define _HAL_H_ 3 | 4 | #include "../../../USER_SETTINGS.h" 5 | 6 | #if defined(HW_LILYGO) 7 | #include "hw_lilygo.h" 8 | #elif defined(HW_STARK) 9 | #include "hw_stark.h" 10 | #elif defined(HW_3LB) 11 | #include "hw_3LB.h" 12 | #elif defined(HW_DEVKIT) 13 | #include "hw_devkit.h" 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Software/src/devboard/hal/hw_devkit.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_DEVKIT_H__ 2 | #define __HW_DEVKIT_H__ 3 | 4 | /* 5 | ESP32 DevKit V1 development board with 30 pins. 6 | For more information, see: https://lastminuteengineers.com/esp32-pinout-reference/. 7 | 8 | The pin layout below supports the following: 9 | - 1x RS485 10 | - 2x CAN (1x via SN65HVD230 (UART), 1x via MCP2515 (SPI)) 11 | - 1x CANFD (via MCP2518FD (SPI)) 12 | */ 13 | 14 | // Board boot-up time 15 | #define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up 16 | 17 | // Core assignment 18 | #define CORE_FUNCTION_CORE 1 19 | #define MODBUS_CORE 0 20 | #define WIFI_CORE 0 21 | 22 | // RS485 23 | #define RS485_TX_PIN GPIO_NUM_1 24 | #define RS485_RX_PIN GPIO_NUM_3 25 | 26 | // CAN settings 27 | #define CAN_1_TYPE ESP32CAN 28 | //#define CAN_2_TYPE MCP2515 29 | //#define CAN_3_TYPE MCP2518FD 30 | 31 | // CAN1 PIN mappings, do not change these unless you are adding on extra hardware to the PCB 32 | #define CAN_TX_PIN GPIO_NUM_27 33 | #define CAN_RX_PIN GPIO_NUM_26 34 | 35 | // CAN_ADDON defines 36 | #define MCP2515_SCK GPIO_NUM_22 // SCK input of MCP2515 37 | #define MCP2515_MOSI GPIO_NUM_21 // SDI input of MCP2515 38 | #define MCP2515_MISO GPIO_NUM_19 // SDO output of MCP2515 39 | #define MCP2515_CS GPIO_NUM_18 // CS input of MCP2515 40 | #define MCP2515_INT GPIO_NUM_23 // INT output of MCP2515 41 | 42 | // CANFD_ADDON defines 43 | #define MCP2517_SCK GPIO_NUM_33 // SCK input of MCP2517 44 | #define MCP2517_SDI GPIO_NUM_32 // SDI input of MCP2517 45 | #define MCP2517_SDO GPIO_NUM_35 // SDO output of MCP2517 | Pin 35 is input only, without pullup/down resistors 46 | #define MCP2517_CS GPIO_NUM_25 // CS input of MCP2517 47 | #define MCP2517_INT GPIO_NUM_34 // INT output of MCP2517 | Pin 34 is input only, without pullup/down resistors 48 | 49 | // Contactor handling 50 | #define POSITIVE_CONTACTOR_PIN GPIO_NUM_5 51 | #define NEGATIVE_CONTACTOR_PIN GPIO_NUM_16 52 | #define PRECHARGE_PIN GPIO_NUM_17 53 | 54 | // SMA CAN contactor pins 55 | #define INVERTER_CONTACTOR_ENABLE_PIN GPIO_NUM_14 56 | 57 | // LED 58 | #define LED_PIN GPIO_NUM_4 59 | #define LED_MAX_BRIGHTNESS 40 60 | #define INVERTER_CONTACTOR_ENABLE_LED_PIN GPIO_NUM_2 61 | 62 | // Equipment stop pin 63 | #define EQUIPMENT_STOP_PIN GPIO_NUM_12 64 | 65 | // Automatic precharging 66 | #define HIA4V1_PIN GPIO_NUM_17 67 | #define INVERTER_DISCONNECT_CONTACTOR_PIN GPIO_NUM_5 68 | 69 | // BMW_I3_BATTERY wake up pin 70 | #ifdef BMW_I3_BATTERY 71 | #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 72 | #ifdef DOUBLE_BATTERY 73 | #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 74 | #endif // DOUBLE_BATTERY 75 | #endif // BMW_I3_BATTERY 76 | 77 | /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ 78 | #ifndef HW_CONFIGURED 79 | #define HW_CONFIGURED 80 | #else 81 | #error Multiple HW defined! Please select a single HW 82 | #endif // HW_CONFIGURED 83 | 84 | #ifdef CHADEMO_BATTERY 85 | #error CHADEMO pins are not defined for this hardware. 86 | #endif // CHADEMO_BATTERY 87 | 88 | #ifdef BMW_I3_BATTERY 89 | #if defined(WUP_PIN1) && defined(CANFD_ADDON) 90 | #error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and a CANFD addon board using these pins. Choose between BMW_I3_BATTERY and CANFD_ADDON 91 | #endif // defined(WUP_PIN1) && defined(CANFD_ADDON) 92 | #if defined(WUP_PIN2) && defined(CANFD_ADDON) 93 | #error GPIO PIN 32 cannot be used for both BMWi3 Wakeup and a CANFD addon board using these pins. Choose between BMW_I3_BATTERY and CANFD_ADDON 94 | #endif // defined(WUP_PIN2) && defined(CANFD_ADDON) 95 | #endif // BMW_I3_BATTERY 96 | 97 | #endif // __HW_DEVKIT_H__ 98 | -------------------------------------------------------------------------------- /Software/src/devboard/hal/hw_stark.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_STARK_H__ 2 | #define __HW_STARK_H__ 3 | 4 | /* 5 | Stark CMR v1 - DIN-rail module with 4 power outputs, 1 x rs485, 1 x can and 1 x can-fd channel. 6 | For more information on this board visit the project discord or contact johan@redispose.se 7 | 8 | GPIOs on extra header 9 | * GPIO 2 10 | * GPIO 17 (only available if can channel 2 is inactive) 11 | * GPIO 19 12 | * GPIO 14 (JTAG TMS) 13 | * GPIO 12 (JTAG TDI) 14 | * GPIO 13 (JTAG TCK) 15 | * GPIO 15 (JTAG TDO) 16 | */ 17 | 18 | // Board boot-up time 19 | #define BOOTUP_TIME 5000 // Time in ms it takes before system is considered fully started up 20 | 21 | // Core assignment 22 | #define CORE_FUNCTION_CORE 1 23 | #define MODBUS_CORE 0 24 | #define WIFI_CORE 0 25 | 26 | // RS485 27 | // #define PIN_5V_EN 16 // Not needed, GPIO 16 has hardware pullup for PSRAM compatibility 28 | // #define RS485_EN_PIN 17 // Not needed, GPIO 17 is used as SCK input of MCP2517 29 | #define RS485_TX_PIN 22 30 | #define RS485_RX_PIN 21 31 | // #define RS485_SE_PIN 19 // Not needed, GPIO 19 is available as extra GPIO via pin header 32 | 33 | // CAN settings 34 | #define CAN_1_TYPE ESP32CAN 35 | 36 | // CAN1 PIN mappings, do not change these unless you are adding on extra hardware to the PCB 37 | #define CAN_TX_PIN GPIO_NUM_27 38 | #define CAN_RX_PIN GPIO_NUM_26 39 | // #define CAN_SE_PIN 23 // (No function, GPIO 23 used instead as MCP_SCK) 40 | 41 | // CANFD_ADDON defines 42 | #define MCP2517_SCK 17 // SCK input of MCP2517 43 | #define MCP2517_SDI 5 // SDI input of MCP2517 44 | #define MCP2517_SDO 34 // SDO output of MCP2517 45 | #define MCP2517_CS 18 // CS input of MCP2517 46 | #define MCP2517_INT 35 // INT output of MCP2517 47 | 48 | // Contactor handling 49 | #define POSITIVE_CONTACTOR_PIN 32 50 | #define NEGATIVE_CONTACTOR_PIN 33 51 | #define PRECHARGE_PIN 25 52 | #define BMS_POWER 23 53 | 54 | // Automatic precharging 55 | #define HIA4V1_PIN 19 //Available as extra GPIO via pin header 56 | #define INVERTER_DISCONNECT_CONTACTOR_PIN 25 57 | 58 | // SMA CAN contactor pins 59 | #define INVERTER_CONTACTOR_ENABLE_PIN 2 60 | 61 | // LED 62 | #define LED_PIN 4 63 | #define LED_MAX_BRIGHTNESS 40 64 | 65 | // Equipment stop pin 66 | #define EQUIPMENT_STOP_PIN 2 67 | 68 | // BMW_I3_BATTERY wake up pin 69 | #ifdef BMW_I3_BATTERY 70 | #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 71 | #ifdef DOUBLE_BATTERY 72 | #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 73 | #endif // DOUBLE_BATTERY 74 | #endif // BMW_I3_BATTERY 75 | 76 | /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ 77 | #ifndef HW_CONFIGURED 78 | #define HW_CONFIGURED 79 | #else 80 | #error Multiple HW defined! Please select a single HW 81 | #endif // HW_CONFIGURED 82 | 83 | #ifdef BMW_I3_BATTERY 84 | #if defined(CONTACTOR_CONTROL) && defined(WUP_PIN1) 85 | #error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL 86 | #endif 87 | #if defined(CONTACTOR_CONTROL) && defined(WUP_PIN2) 88 | #error GPIO PIN 32 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL 89 | #endif 90 | #endif // BMW_I3_BATTERY 91 | 92 | #endif // __HW_STARK_H__ 93 | -------------------------------------------------------------------------------- /Software/src/devboard/mqtt/mqtt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MQTT add-on for the battery emulator 3 | * 4 | * Usage: 5 | * 6 | * Subscription - Add topics to MQTT_SUBSCRIPTIONS in USER_SETTINGS.h and handle the messages in mqtt.cpp:callback() 7 | * 8 | * Publishing - See example in mqtt.cpp:publish_values() for constructing the payload 9 | * 10 | * Home assistant - See below for an example, and the official documentation is quite good (https://www.home-assistant.io/integrations/sensor.mqtt/) 11 | * in configuration.yaml: 12 | * mqtt: !include mqtt.yaml 13 | * 14 | * in mqtt.yaml: 15 | * sensor: 16 | * - name: "Cell max" 17 | * state_topic: "battery/info" 18 | * unit_of_measurement: "mV" 19 | * value_template: "{{ value_json.cell_max_voltage | int }}" 20 | * - name: "Cell min" 21 | * state_topic: "battery/info" 22 | * unit_of_measurement: "mV" 23 | * value_template: "{{ value_json.cell_min_voltage | int }}" 24 | * - name: "Temperature max" 25 | * state_topic: "battery/info" 26 | * unit_of_measurement: "C" 27 | * value_template: "{{ value_json.temperature_max | float }}" 28 | * - name: "Temperature min" 29 | * state_topic: "battery/info" 30 | * unit_of_measurement: "C" 31 | * value_template: "{{ value_json.temperature_min | float }}" 32 | */ 33 | 34 | #ifndef __MQTT_H__ 35 | #define __MQTT_H__ 36 | 37 | #include 38 | #include 39 | #include "../../include.h" 40 | 41 | #define MQTT_MSG_BUFFER_SIZE (1024) 42 | 43 | extern const char* version_number; // The current software version, used for mqtt 44 | 45 | extern const char* mqtt_user; 46 | extern const char* mqtt_password; 47 | extern const char* mqtt_topic_name; 48 | extern const char* mqtt_object_id_prefix; 49 | extern const char* mqtt_device_name; 50 | extern const char* ha_device_id; 51 | 52 | extern char mqtt_msg[MQTT_MSG_BUFFER_SIZE]; 53 | 54 | void init_mqtt(void); 55 | void mqtt_loop(void); 56 | bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /Software/src/devboard/safety/safety.h: -------------------------------------------------------------------------------- 1 | #ifndef SAFETY_H 2 | #define SAFETY_H 3 | #include 4 | #include 5 | #include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" 6 | 7 | #define MAX_CAN_FAILURES 50 8 | 9 | #define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 5 10 | 11 | //battery pause status begin 12 | enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 }; 13 | extern bool emulator_pause_request_ON; 14 | extern bool emulator_pause_CAN_send_ON; 15 | extern battery_pause_status emulator_pause_status; 16 | extern bool allowed_to_send_CAN; 17 | //battery pause status end 18 | 19 | extern void store_settings_equipment_stop(); 20 | 21 | void update_machineryprotection(); 22 | 23 | //battery pause status begin 24 | void setBatteryPause(bool pause_battery, bool pause_CAN, bool equipment_stop = false, bool store_settings = true); 25 | void update_pause_state(); 26 | std::string get_emulator_pause_status(); 27 | //battery pause status end 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Software/src/devboard/sdcard/sdcard.h: -------------------------------------------------------------------------------- 1 | #ifndef SDCARD_H 2 | #define SDCARD_H 3 | 4 | #include 5 | #include "../../communication/can/comm_can.h" 6 | #include "../hal/hal.h" 7 | #include "../utils/events.h" 8 | 9 | #if defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && \ 10 | defined(SD_MISO_PIN) // ensure code is only compiled if all SD card pins are defined 11 | #define CAN_LOG_FILE "/canlog.txt" 12 | #define LOG_FILE "/log.txt" 13 | 14 | void init_logging_buffers(); 15 | 16 | void init_sdcard(); 17 | void log_sdcard_details(); 18 | 19 | void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir); 20 | void write_can_frame_to_sdcard(); 21 | 22 | void pause_can_writing(); 23 | void resume_can_writing(); 24 | void delete_can_log(); 25 | void delete_log(); 26 | void resume_log_writing(); 27 | void pause_log_writing(); 28 | 29 | void add_log_to_buffer(const uint8_t* buffer, size_t size); 30 | void write_log_to_sdcard(); 31 | 32 | #endif // defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && defined(SD_MISO_PIN) 33 | #endif // SDCARD_H 34 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/debounce_button.cpp: -------------------------------------------------------------------------------- 1 | #include "debounce_button.h" 2 | 3 | // Function to initialize the debounced button with pin, switch type, and debounce delay 4 | void initDebouncedButton(DebouncedButton& button, int pin, SwitchType type, unsigned long debounceDelay) { 5 | button.pin = pin; 6 | button.debounceDelay = debounceDelay; 7 | button.lastDebounceTime = 0; 8 | button.lastButtonState = (type == NC) ? HIGH : LOW; // NC starts HIGH, NO starts LOW 9 | button.buttonState = button.lastButtonState; 10 | button.switchType = type; 11 | pinMode(pin, INPUT); // Setup pin mode 12 | } 13 | 14 | ButtonState debounceButton(DebouncedButton& button, unsigned long& timeSincePress) { 15 | int reading = digitalRead(button.pin); 16 | 17 | // If the button state has changed due to noise or a press 18 | if (reading != button.lastButtonState) { 19 | // Reset debounce timer 20 | button.lastDebounceTime = millis(); 21 | } 22 | 23 | // Check if the state change is stable for the debounce delay 24 | if ((millis() - button.lastDebounceTime) > button.debounceDelay) { 25 | if (reading != button.buttonState) { 26 | button.buttonState = reading; 27 | 28 | // Adjust button logic based on switch type (NC or NO) 29 | if (button.switchType == NC) { 30 | if (button.buttonState == LOW) { 31 | // Button pressed for NC, record the press time 32 | button.ulPressTime = millis(); 33 | return PRESSED; 34 | } else { 35 | // Button released for NC, calculate the time since last press 36 | timeSincePress = millis() - button.ulPressTime; 37 | return RELEASED; 38 | } 39 | } else { // NO type 40 | if (button.buttonState == HIGH) { 41 | // Button pressed for NO, record the press time 42 | button.ulPressTime = millis(); 43 | return PRESSED; 44 | } else { 45 | // Button released for NO, calculate the time since last press 46 | timeSincePress = millis() - button.ulPressTime; 47 | return RELEASED; 48 | } 49 | } 50 | } 51 | } 52 | 53 | // Remember the last button state 54 | button.lastButtonState = reading; 55 | return NONE; // No change in button state 56 | } 57 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/debounce_button.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBOUNCE_H 2 | #define DEBOUNCE_H 3 | 4 | #include 5 | 6 | // Enum to define switch type (Normally Closed - NC, Normally Open - NO) 7 | enum SwitchType { 8 | NC, // Normally Closed 9 | NO // Normally Open 10 | }; 11 | 12 | // Enum to define button state 13 | enum ButtonState { 14 | NONE, // No change in button state 15 | PRESSED, // Button is pressed down 16 | RELEASED // Button is released 17 | }; 18 | 19 | // Struct to hold button state and debounce parameters 20 | struct DebouncedButton { 21 | int pin; // GPIO pin number 22 | unsigned long lastDebounceTime; // Time of last state change 23 | unsigned long debounceDelay; // Debounce delay time 24 | unsigned long ulPressTime; // Time when the button was last pressed 25 | bool lastButtonState; // Previous button state 26 | bool buttonState; // Current button state 27 | SwitchType switchType; // Switch type (NC or NO) 28 | }; 29 | 30 | // Function to initialize the debounced button 31 | void initDebouncedButton(DebouncedButton& button, int pin, SwitchType type, unsigned long debounceDelay = 50); 32 | 33 | // Function to debounce button and return the button state (PRESSED, RELEASED, or NONE) 34 | ButtonState debounceButton(DebouncedButton& button, unsigned long& timeSincePress); 35 | 36 | #endif // DEBOUNCE_H 37 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/led_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef LED_H_ 2 | #define LED_H_ 3 | 4 | #include "../../include.h" 5 | #include "../../lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h" 6 | 7 | class LED { 8 | public: 9 | led_color color = led_color::GREEN; 10 | 11 | LED() 12 | : pixels(1, LED_PIN, NEO_GRB), 13 | max_brightness(LED_MAX_BRIGHTNESS), 14 | brightness(LED_MAX_BRIGHTNESS), 15 | mode(led_mode_enum::CLASSIC) {} 16 | 17 | LED(led_mode_enum mode) 18 | : pixels(1, LED_PIN, NEO_GRB), max_brightness(LED_MAX_BRIGHTNESS), brightness(LED_MAX_BRIGHTNESS), mode(mode) {} 19 | 20 | void exe(void); 21 | void init(void) { pixels.begin(); } 22 | 23 | private: 24 | Adafruit_NeoPixel pixels; 25 | uint8_t max_brightness; 26 | uint8_t brightness; 27 | led_mode_enum mode; 28 | 29 | void classic_run(void); 30 | void flow_run(void); 31 | void heartbeat_run(void); 32 | 33 | uint8_t up_down(float middle_point_f); 34 | }; 35 | 36 | void led_init(void); 37 | void led_exe(void); 38 | led_color led_get_color(void); 39 | 40 | #endif // LED_H_ 41 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOGGING_H__ 2 | #define __LOGGING_H__ 3 | 4 | #include 5 | #include "Print.h" 6 | #include "types.h" 7 | 8 | class Logging : public Print { 9 | void add_timestamp(size_t size); 10 | 11 | public: 12 | virtual size_t write(const uint8_t* buffer, size_t size); 13 | virtual size_t write(uint8_t) { return 0; } 14 | void printf(const char* fmt, ...); 15 | void log_bms_status(real_bms_status_enum bms_status); 16 | Logging() {} 17 | }; 18 | 19 | extern Logging logging; 20 | #endif // __LOGGING_H__ 21 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/ntp_time.cpp: -------------------------------------------------------------------------------- 1 | #include "ntp_time.h" 2 | #include "../../include.h" 3 | #include "time.h" 4 | 5 | const unsigned long millisInADay = 24 * 60 * 60 * 1000; // 24 hours in milliseconds 6 | 7 | unsigned long long getNtpTimeInMillis() { 8 | configTzTime(time_zone, ntpServer1, ntpServer2); 9 | struct tm timeinfo; 10 | 11 | // Wait for time to be set 12 | for (int i = 0; i < 5; i++) { 13 | if (getLocalTime(&timeinfo)) { 14 | logging.println("Time retrieved from NTP Server"); 15 | break; 16 | } 17 | logging.println("Waiting for NTP time..."); 18 | } 19 | 20 | if (!getLocalTime(&timeinfo)) { 21 | logging.println("Failed to obtain time"); 22 | return 0; 23 | } 24 | 25 | // Convert to milliseconds 26 | time_t epochTime = mktime(&timeinfo); 27 | return static_cast(epochTime) * 1000; 28 | } 29 | 30 | // Function to calculate the difference in milliseconds to the next target time 31 | unsigned long long millisToNextTargetTime(unsigned long long currentMillis, int targetTime) { 32 | int hour = targetTime / 100; 33 | int minute = targetTime % 100; 34 | 35 | time_t currentTime = currentMillis / 1000; // Convert milliseconds to seconds 36 | struct tm* timeinfo = localtime(¤tTime); 37 | 38 | // Set timeinfo to the target time on the next day 39 | timeinfo->tm_hour = hour; 40 | timeinfo->tm_min = minute; 41 | timeinfo->tm_sec = 0; 42 | 43 | // Increment day if the current time is past the target time 44 | if (mktime(timeinfo) <= currentTime) { 45 | timeinfo->tm_mday += 1; 46 | } 47 | time_t nextTargetTime = mktime(timeinfo); 48 | unsigned long long nextTargetMillis = static_cast(nextTargetTime) * 1000; 49 | return nextTargetMillis - currentMillis; 50 | } 51 | 52 | unsigned long long getTimeOffsetfromNowUntil(int targetTime) { 53 | logging.println("Getting time offset from now until " + String(targetTime)); 54 | unsigned long long timeinMillis = getNtpTimeInMillis(); 55 | if (timeinMillis != 0) { 56 | logging.println("Time in millis: " + String(timeinMillis)); 57 | unsigned long long timeOffsetUntilTargetTime = millisInADay - (millisToNextTargetTime(timeinMillis, targetTime)); 58 | logging.println("Time offset until target time: " + String(timeOffsetUntilTargetTime)); 59 | return timeOffsetUntilTargetTime; 60 | } else 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/ntp_time.h: -------------------------------------------------------------------------------- 1 | #ifndef __NTPTIME_H__ 2 | #define __NTPTIME_H__ 3 | 4 | extern const char* ntpServer1; 5 | extern const char* ntpServer2; 6 | extern const char* time_zone; 7 | 8 | unsigned long long getNtpTimeInMillis(); 9 | unsigned long long millisToNextTargetTime(unsigned long long currentMillis, int targetTime); 10 | unsigned long long getTimeOffsetfromNowUntil(int targetTime); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/time_meas.h: -------------------------------------------------------------------------------- 1 | #ifndef TIME_MEAS_H_ 2 | #define TIME_MEAS_H_ 3 | 4 | #include "esp_timer.h" 5 | 6 | /** Start time measurement in microseconds 7 | * Input parameter must be a unique "tag", e.g: START_TIME_MEASUREMENT(wifi); 8 | */ 9 | #ifdef FUNCTION_TIME_MEASUREMENT 10 | #define START_TIME_MEASUREMENT(x) int64_t start_time_##x = esp_timer_get_time() 11 | /** End time measurement in microseconds 12 | * Input parameters are the unique tag and the name of the ALREADY EXISTING 13 | * destination variable (int64_t), e.g: END_TIME_MEASUREMENT(wifi, my_wifi_time_int64_t); 14 | */ 15 | #define END_TIME_MEASUREMENT(x, y) y = esp_timer_get_time() - start_time_##x 16 | /** End time measurement in microseconds, log maximum 17 | * Input parameters are the unique tag and the name of the ALREADY EXISTING 18 | * destination variable (int64_t), e.g: END_TIME_MEASUREMENT_MAX(wifi, my_wifi_time_int64_t); 19 | * 20 | * This will log the maximum value in the destination variable. 21 | */ 22 | #define END_TIME_MEASUREMENT_MAX(x, y) y = MAX(y, esp_timer_get_time() - start_time_##x) 23 | #else 24 | #define START_TIME_MEASUREMENT(x) ; 25 | #define END_TIME_MEASUREMENT(x, y) ; 26 | #define END_TIME_MEASUREMENT_MAX(x, y) ; 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | MyTimer::MyTimer(unsigned long interval) : interval(interval) { 4 | previous_millis = millis(); 5 | } 6 | 7 | bool MyTimer::elapsed(void) { 8 | unsigned long current_millis = millis(); 9 | if (current_millis - previous_millis >= interval) { 10 | previous_millis = current_millis; 11 | return true; 12 | } 13 | return false; 14 | } 15 | 16 | void MyTimer::reset(void) { 17 | previous_millis = millis(); 18 | } 19 | 20 | void MyTimer::set_interval(unsigned long interval) { 21 | this->interval = interval; 22 | reset(); 23 | } 24 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYTIMER_H__ 2 | #define __MYTIMER_H__ 3 | 4 | #ifndef UNIT_TEST 5 | #include 6 | #endif 7 | 8 | class MyTimer { 9 | public: 10 | /** Default constructor */ 11 | MyTimer() : interval(0), previous_millis(0) {} 12 | 13 | /** interval in ms */ 14 | MyTimer(unsigned long interval); 15 | /** Returns true and resets the timer if it has elapsed */ 16 | bool elapsed(void); 17 | void reset(void); 18 | void set_interval(unsigned long interval); 19 | 20 | unsigned long interval; 21 | unsigned long previous_millis; 22 | 23 | private: 24 | }; 25 | 26 | #endif // __MYTIMER_H__ 27 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | // Function to get string representation of bms_status_enum 4 | std::string getBMSStatus(bms_status_enum status) { 5 | switch (status) { 6 | case STANDBY: 7 | return "STANDBY"; 8 | case INACTIVE: 9 | return "INACTIVE"; 10 | case DARKSTART: 11 | return "DARKSTART"; 12 | case ACTIVE: 13 | return "ACTIVE"; 14 | case FAULT: 15 | return "FAULT"; 16 | case UPDATING: 17 | return "UPDATING"; 18 | default: 19 | return "UNKNOWN"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _TYPES_H_ 2 | #define _TYPES_H_ 3 | 4 | #include 5 | 6 | enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 }; 7 | enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 }; 8 | enum battery_chemistry_enum { NCA, NMC, LFP }; 9 | enum led_color { GREEN, YELLOW, RED, BLUE }; 10 | enum led_mode_enum { CLASSIC, FLOW, HEARTBEAT }; 11 | enum PrechargeState { 12 | AUTO_PRECHARGE_IDLE, 13 | AUTO_PRECHARGE_START, 14 | AUTO_PRECHARGE_PRECHARGING, 15 | AUTO_PRECHARGE_OFF, 16 | AUTO_PRECHARGE_COMPLETED 17 | }; 18 | 19 | #define DISCHARGING 1 20 | #define CHARGING 2 21 | 22 | #define INTERVAL_10_MS 10 23 | #define INTERVAL_20_MS 20 24 | #define INTERVAL_30_MS 30 25 | #define INTERVAL_40_MS 40 26 | #define INTERVAL_50_MS 50 27 | #define INTERVAL_70_MS 70 28 | #define INTERVAL_100_MS 100 29 | #define INTERVAL_200_MS 200 30 | #define INTERVAL_250_MS 250 31 | #define INTERVAL_500_MS 500 32 | #define INTERVAL_640_MS 640 33 | #define INTERVAL_1_S 1000 34 | #define INTERVAL_2_S 2000 35 | #define INTERVAL_5_S 5000 36 | #define INTERVAL_10_S 10000 37 | #define INTERVAL_60_S 60000 38 | 39 | #define INTERVAL_10_MS_DELAYED 15 40 | 41 | #define CAN_STILL_ALIVE 60 42 | // Set by battery each time we get a CAN message. Decrements every second. When reaching 0, sets event 43 | 44 | /* CAN Frame structure */ 45 | typedef struct { 46 | bool FD; 47 | bool ext_ID; 48 | uint8_t DLC; 49 | uint32_t ID; 50 | union { 51 | uint8_t u8[64]; 52 | uint32_t u32[2]; 53 | uint64_t u64; 54 | } data; 55 | } CAN_frame; 56 | 57 | enum frameDirection { MSG_RX, MSG_TX }; //RX = 0, TX = 1 58 | 59 | typedef struct { 60 | CAN_frame frame; 61 | frameDirection direction; 62 | } CAN_log_frame; 63 | 64 | std::string getBMSStatus(bms_status_enum status); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Software/src/devboard/utils/value_mapping.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAPPING_H__ 2 | #define __MAPPING_H__ 3 | 4 | #include "../../include.h" 5 | 6 | /** MIN macro, returns the smallest of two values 7 | * Warning: Side effects. MUST be called using values and/or variables only, e.g.: 8 | * int x = MIN(temp1, temp2); 9 | * int x = MIN(reported_temp, 25); 10 | * Do NOT use this macro with operators or function calls, e.g.: 11 | * int x = MIN(y++, my_func()); 12 | */ 13 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 14 | /** MAX macro, returns the largest of two values 15 | * Warning: Side effects. MUST be called using values and/or variables only, e.g.: 16 | * int x = MAX(temp1, temp2); 17 | * int x = MAX(reported_temp, 10); 18 | * Do NOT use this macro with operators or function calls, e.g.: 19 | * int x = MAX(y++, my_func()); 20 | */ 21 | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 22 | /** CONSTRAIN macro, limits a value to a provided range 23 | * Warning: Side effects. MUST be called using values and/or variables only, e.g.: 24 | * int x = CONSTRAIN(temp, min_temp, max_temp); 25 | * int x = CONSTRAIN(reported_temp, 10, 30); 26 | * Do NOT use this macro with operators or function calls, e.g.: 27 | * int x = CONSTRAIN(y++, my_min_func(), max_value--); 28 | */ 29 | #define CONSTRAIN(val, min, max) (MIN(max, MAX(min, val))) 30 | 31 | static inline float map_float(float val, float in_min, float in_max, float out_min, float out_max) { 32 | if (val <= in_min) { 33 | return out_min; 34 | } else if (val > in_max) { 35 | return out_max; 36 | } else { 37 | return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 38 | } 39 | } 40 | 41 | static inline uint8_t map_uint8(uint8_t val, uint8_t in_min, uint8_t in_max, uint8_t out_min, uint8_t out_max) { 42 | if (val <= in_min) { 43 | return out_min; 44 | } else if (val >= in_max) { 45 | return out_max; 46 | } else { 47 | // Ensure the multiplication does not overflow by promoting to a larger type (uint16_t) 48 | // And ensure the division happens last to minimize precision loss 49 | return static_cast((static_cast(val - in_min) * (out_max - out_min)) / (in_max - in_min) + 50 | out_min); 51 | } 52 | } 53 | 54 | static inline uint16_t map_uint16(uint16_t val, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) { 55 | if (val <= in_min) { 56 | return out_min; 57 | } else if (val >= in_max) { 58 | return out_max; 59 | } else { 60 | // Promote to uint32_t to prevent overflow during multiplication 61 | return static_cast((static_cast(val - in_min) * (out_max - out_min)) / (in_max - in_min) + 62 | out_min); 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/BatteryHtmlRenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef _BATTERY_HTML_RENDERER_H 2 | #define _BATTERY_HTML_RENDERER_H 3 | 4 | #include 5 | 6 | // Each battery can implement this interface to render more battery specific HTML 7 | // content 8 | class BatteryHtmlRenderer { 9 | public: 10 | virtual String get_status_html() = 0; 11 | }; 12 | 13 | class BatteryDefaultRenderer : public BatteryHtmlRenderer { 14 | public: 15 | String get_status_html() { return String("No extra information available for this battery type"); } 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/advanced_battery_html.h: -------------------------------------------------------------------------------- 1 | #ifndef ADVANCEDBATTERY_H 2 | #define ADVANCEDBATTERY_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Replaces placeholder with content section in web page 9 | * 10 | * @param[in] var 11 | * 12 | * @return String 13 | */ 14 | String advanced_battery_processor(const String& var); 15 | 16 | class Battery; 17 | 18 | // Each BatteryCommand defines a command that can be performed by a battery. 19 | // Whether the selected battery supports the command is determined at run-time 20 | // by calling the condition callback. 21 | struct BatteryCommand { 22 | // The unique name of the route in the API to execute the command or a function in Javascript 23 | const char* identifier; 24 | 25 | // Display name for the command. Can be used in the UI. 26 | const char* title; 27 | 28 | // Are you sure? prompt text. If null, no confirmation is asked. 29 | const char* prompt; 30 | 31 | // Function to determine whether the given battery supports this command. 32 | std::function condition; 33 | 34 | // Function that executes the command for the given battery. 35 | std::function action; 36 | }; 37 | 38 | extern std::vector battery_commands; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/can_logging_html.cpp: -------------------------------------------------------------------------------- 1 | #include "can_logging_html.h" 2 | #include 3 | #include "../../datalayer/datalayer.h" 4 | #include "index_html.h" 5 | 6 | String can_logger_processor(void) { 7 | if (!datalayer.system.info.can_logging_active) { 8 | datalayer.system.info.logged_can_messages_offset = 0; 9 | datalayer.system.info.logged_can_messages[0] = '\0'; 10 | } 11 | datalayer.system.info.can_logging_active = 12 | true; // Signal to main loop that we should log messages. Disabled by default for performance reasons 13 | String content = index_html_header; 14 | // Page format 15 | content += ""; 25 | content += " "; 26 | content += " "; 27 | #ifdef LOG_CAN_TO_SD 28 | content += " "; 29 | #endif 30 | content += ""; 31 | 32 | // Start a new block for the CAN messages 33 | content += "
"; 34 | 35 | // Check for messages 36 | if (datalayer.system.info.logged_can_messages[0] == 0) { 37 | content += "CAN logger started! Refresh page to display incoming(RX) and outgoing(TX) messages"; 38 | } else { 39 | // Split the messages using the newline character 40 | String messages = String(datalayer.system.info.logged_can_messages); 41 | int startIndex = 0; 42 | int endIndex = messages.indexOf('\n'); 43 | while (endIndex != -1) { 44 | // Extract a single message and wrap it in a styled div 45 | String singleMessage = messages.substring(startIndex, endIndex); 46 | content += "
" + singleMessage + "
"; 47 | startIndex = endIndex + 1; // Move past the newline character 48 | endIndex = messages.indexOf('\n', startIndex); 49 | } 50 | } 51 | 52 | content += "
"; 53 | 54 | // Add JavaScript for navigation 55 | content += ""; 65 | content += index_html_footer; 66 | return content; 67 | } 68 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/can_logging_html.h: -------------------------------------------------------------------------------- 1 | #ifndef CANLOGGER_H 2 | #define CANLOGGER_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Replaces placeholder with content section in web page 9 | * 10 | * @param[in] var 11 | * 12 | * @return String 13 | */ 14 | String can_logger_processor(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/can_replay_html.h: -------------------------------------------------------------------------------- 1 | #ifndef CANREPLAY_H 2 | #define CANREPLAY_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Replaces placeholder with content section in web page 9 | * 10 | * @param[in] var 11 | * 12 | * @return String 13 | */ 14 | String can_replay_processor(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/cellmonitor_html.h: -------------------------------------------------------------------------------- 1 | #ifndef CELLMONITOR_H 2 | #define CELLMONITOR_H 3 | 4 | #include "../../include.h" 5 | 6 | /** 7 | * @brief Replaces placeholder with content section in web page 8 | * 9 | * @param[in] var 10 | * 11 | * @return String 12 | */ 13 | String cellmonitor_processor(const String& var); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/debug_logging_html.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_logging_html.h" 2 | #include 3 | #include "../../datalayer/datalayer.h" 4 | #include "index_html.h" 5 | 6 | #if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD) 7 | String debug_logger_processor(void) { 8 | String content = String(index_html_header); 9 | // Page format 10 | content += ""; 20 | #ifdef DEBUG_VIA_WEB 21 | content += " "; 22 | #endif 23 | content += " "; 24 | #ifdef LOG_TO_SD 25 | content += " "; 26 | #endif 27 | content += ""; 28 | 29 | // Start a new block for the debug log messages 30 | content += "
";
31 |   content += String(datalayer.system.info.logged_can_messages);
32 |   content += "
"; 33 | 34 | // Add JavaScript for navigation 35 | content += ""; 43 | content += index_html_footer; 44 | return content; 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/debug_logging_html.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGLOGGER_H 2 | #define DEBUGLOGGER_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Replaces placeholder with content section in web page 9 | * 10 | * @param[in] var 11 | * 12 | * @return String 13 | */ 14 | String debug_logger_processor(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/events_html.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTS_H 2 | #define EVENTS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../utils/events.h" 8 | 9 | /** 10 | * @brief Replaces placeholder with content section in web page 11 | * 12 | * @param[in] var 13 | * 14 | * @return String 15 | */ 16 | String events_processor(const String& var); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/index_html.cpp: -------------------------------------------------------------------------------- 1 | #include "index_html.h" 2 | 3 | #define INDEX_HTML_HEADER \ 4 | R"rawliteral(Battery Emulator)rawliteral" 5 | #define INDEX_HTML_FOOTER R"rawliteral()rawliteral"; 6 | 7 | const char index_html[] = INDEX_HTML_HEADER "%X%" INDEX_HTML_FOOTER; 8 | const char index_html_header[] = INDEX_HTML_HEADER; 9 | const char index_html_footer[] = INDEX_HTML_FOOTER; 10 | 11 | /* The above code is minified (https://kangax.github.io/html-minifier/) to increase performance. Here is the full HTML function: 12 | 13 | 14 | Battery Emulator 15 | 16 | 21 | 22 | 23 | %X% 24 | 25 | 26 | */ 27 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/index_html.h: -------------------------------------------------------------------------------- 1 | #ifndef INDEX_HTML_H 2 | #define INDEX_HTML_H 3 | 4 | extern const char index_html[]; 5 | extern const char index_html_header[]; 6 | extern const char index_html_footer[]; 7 | 8 | #endif // INDEX_HTML_H 9 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/settings_html.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_H 2 | #define SETTINGS_H 3 | 4 | #include 5 | #include 6 | 7 | extern std::string ssid; 8 | extern std::string password; 9 | 10 | #include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password 11 | 12 | /** 13 | * @brief Replaces placeholder with content section in web page 14 | * 15 | * @param[in] var 16 | * 17 | * @return String 18 | */ 19 | String settings_processor(const String& var); 20 | /** 21 | * @brief Maps the value to a string of characters 22 | * 23 | * @param[in] char 24 | * 25 | * @return String 26 | */ 27 | const char* getCANInterfaceName(CAN_Interface interface); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Software/src/devboard/webserver/webserver.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSERVER_H 2 | #define WEBSERVER_H 3 | 4 | #include 5 | #include 6 | #include "../../include.h" 7 | #include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h" 8 | #include "../../lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h" 9 | #include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h" 10 | #include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h" 11 | 12 | extern const char* version_number; // The current software version, shown on webserver 13 | 14 | #include 15 | extern const char* http_username; 16 | extern const char* http_password; 17 | 18 | extern const char* ssidAP; 19 | 20 | // Common charger parameters 21 | extern float charger_stat_HVcur; 22 | extern float charger_stat_HVvol; 23 | extern float charger_stat_ACcur; 24 | extern float charger_stat_ACvol; 25 | extern float charger_stat_LVcur; 26 | extern float charger_stat_LVvol; 27 | 28 | //LEAF charger 29 | extern uint16_t OBC_Charge_Power; 30 | 31 | /** 32 | * @brief Initialization function for the webserver. 33 | * 34 | * @param[in] void 35 | * 36 | * @return void 37 | */ 38 | void init_webserver(); 39 | 40 | // /** 41 | // * @brief Function to handle WiFi reconnection. 42 | // * 43 | // * @param[in] void 44 | // * 45 | // * @return void 46 | // */ 47 | // void handle_WiFi_reconnection(unsigned long currentMillis); 48 | 49 | /** 50 | * @brief Initialization function for ElegantOTA. 51 | * 52 | * @param[in] void 53 | * 54 | * @return void 55 | */ 56 | void init_ElegantOTA(); 57 | 58 | /** 59 | * @brief Replaces placeholder with content section in web page 60 | * 61 | * @param[in] var 62 | * 63 | * @return String 64 | */ 65 | String processor(const String& var); 66 | String get_firmware_info_processor(const String& var); 67 | 68 | /** 69 | * @brief Executes on OTA start 70 | * 71 | * @param[in] void 72 | * 73 | * @return void 74 | */ 75 | void onOTAStart(); 76 | 77 | /** 78 | * @brief Executes on OTA progress 79 | * 80 | * @param[in] current Current bytes 81 | * @param[in] final Final bytes 82 | * 83 | * @return void 84 | */ 85 | void onOTAProgress(size_t current, size_t final); 86 | 87 | /** 88 | * @brief Executes on OTA end 89 | * 90 | * @param[in] void 91 | * 92 | * @return bool success: success = true, failed = false 93 | */ 94 | void onOTAEnd(bool success); 95 | 96 | /** 97 | * @brief Formats power values 98 | * 99 | * @param[in] float or uint16_t 100 | * 101 | * @return string: values 102 | */ 103 | template 104 | String formatPowerValue(String label, T value, String unit, int precision, String color = "white"); 105 | 106 | template // This function makes power values appear as W when under 1000, and kW when over 107 | String formatPowerValue(T value, String unit, int precision); 108 | 109 | extern void store_settings(); 110 | 111 | void ota_monitor(); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /Software/src/devboard/wifi/wifi.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFI_H 2 | #define WIFI_H 3 | 4 | #include 5 | #include 6 | #include "../../include.h" 7 | 8 | #ifdef MDNSRESPONDER 9 | #include 10 | #endif // MDNSRESONDER 11 | 12 | extern std::string ssid; 13 | extern std::string password; 14 | extern const uint8_t wifi_channel; 15 | extern const char* ssidAP; 16 | extern const char* passwordAP; 17 | 18 | void init_WiFi(); 19 | void wifi_monitor(); 20 | void connectToWiFi(); 21 | void FullReconnectToWiFi(); 22 | void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info); 23 | void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info); 24 | void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info); 25 | 26 | #ifdef WIFIAP 27 | void init_WiFi_AP(); 28 | #endif // WIFIAP 29 | 30 | #ifdef MDNSRESPONDER 31 | // Initialise mDNS 32 | void init_mDNS(); 33 | #endif // MDNSRESPONDER 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Software/src/include.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_H_ 2 | #define INCLUDE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../USER_SETTINGS.h" 8 | #include "system_settings.h" 9 | 10 | #include "devboard/hal/hal.h" 11 | #include "devboard/safety/safety.h" 12 | #include "devboard/utils/logging.h" 13 | #include "devboard/utils/time_meas.h" 14 | #include "devboard/utils/types.h" 15 | 16 | #include "battery/BATTERIES.h" 17 | #include "charger/CHARGERS.h" 18 | #include "inverter/INVERTERS.h" 19 | 20 | /* - ERROR CHECKS BELOW, DON'T TOUCH - */ 21 | 22 | #if !defined(HW_CONFIGURED) 23 | #error You must select a target hardware in the USER_SETTINGS.h file! 24 | #endif 25 | 26 | #ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN 27 | #if !defined(CANFD_ADDON) 28 | // Check that user did not try to use classic CAN over FD, without FD component 29 | #error PLEASE ENABLE CANFD_ADDON TO USE CLASSIC CAN OVER CANFD INTERFACE 30 | #endif 31 | #endif 32 | 33 | #ifdef HW_LILYGO 34 | #if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) 35 | #if defined(CAN_ADDON) || defined(CANFD_ADDON) || defined(CHADEMO_BATTERY) 36 | //Check that BMS reset is not used at the same time as Chademo and CAN addons 37 | #error BMS RESET CANNOT BE USED AT SAME TIME AS CAN-ADDONS / CHADMEO! NOT ENOUGH GPIO! 38 | #endif 39 | #endif 40 | #endif 41 | 42 | #ifndef BATTERY_SELECTED 43 | #error No battery selected! Choose one from the USER_SETTINGS.h file 44 | #endif 45 | 46 | #if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD) 47 | #if !defined(HW_LILYGO) 48 | #error The SD card logging feature is only available on LilyGo hardware 49 | #endif 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /Software/src/inverter/BYD-MODBUS.h: -------------------------------------------------------------------------------- 1 | #ifndef BYD_MODBUS_H 2 | #define BYD_MODBUS_H 3 | #include "../include.h" 4 | 5 | #ifdef BYD_MODBUS 6 | #define MODBUS_INVERTER_SELECTED 7 | #define SELECTED_INVERTER_CLASS BydModbusInverter 8 | #endif 9 | 10 | #include "ModbusInverterProtocol.h" 11 | 12 | class BydModbusInverter : public ModbusInverterProtocol { 13 | public: 14 | void setup(); 15 | void update_values(); 16 | 17 | private: 18 | void handle_static_data(); 19 | void verify_temperature(); 20 | void verify_inverter_modbus(); 21 | void handle_update_data_modbusp201_byd(); 22 | void handle_update_data_modbusp301_byd(); 23 | 24 | //BYD Modbus specific value 25 | const uint16_t MAX_POWER = 40960; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Software/src/inverter/CanInverterProtocol.h: -------------------------------------------------------------------------------- 1 | #ifndef CANINVERTER_PROTOCOL_H 2 | #define CANINVERTER_PROTOCOL_H 3 | 4 | #include "InverterProtocol.h" 5 | 6 | #include "src/devboard/utils/types.h" 7 | 8 | class CanInverterProtocol : public InverterProtocol { 9 | public: 10 | virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); } 11 | virtual void transmit_can(unsigned long currentMillis) = 0; 12 | virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Software/src/inverter/INVERTERS.cpp: -------------------------------------------------------------------------------- 1 | #include "../include.h" 2 | 3 | // These functions adapt the old C-style global functions inverter-API to the 4 | // object-oriented inverter protocol API. 5 | 6 | InverterProtocol* inverter = nullptr; 7 | 8 | #ifdef CAN_INVERTER_SELECTED 9 | CanInverterProtocol* can_inverter; 10 | #endif 11 | 12 | #ifdef MODBUS_INVERTER_SELECTED 13 | ModbusInverterProtocol* modbus_inverter; 14 | #endif 15 | 16 | void setup_inverter() { 17 | #ifdef MODBUS_INVERTER_SELECTED 18 | modbus_inverter = new SELECTED_INVERTER_CLASS(); 19 | inverter = modbus_inverter; 20 | #endif 21 | 22 | #ifdef CAN_INVERTER_SELECTED 23 | can_inverter = new SELECTED_INVERTER_CLASS(); 24 | inverter = can_inverter; 25 | #endif 26 | 27 | #ifdef RS485_INVERTER_SELECTED 28 | inverter = new SELECTED_INVERTER_CLASS(); 29 | #endif 30 | 31 | if (inverter) { 32 | inverter->setup(); 33 | } 34 | } 35 | 36 | #ifdef CAN_INVERTER_SELECTED 37 | void update_values_can_inverter() { 38 | can_inverter->update_values(); 39 | } 40 | 41 | void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { 42 | can_inverter->map_can_frame_to_variable(rx_frame); 43 | } 44 | 45 | void transmit_can_inverter(unsigned long currentMillis) { 46 | can_inverter->transmit_can(currentMillis); 47 | } 48 | #endif 49 | 50 | #ifdef RS485_INVERTER_SELECTED 51 | void receive_RS485() { 52 | ((Rs485InverterProtocol*)inverter)->receive_RS485(); 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /Software/src/inverter/INVERTERS.h: -------------------------------------------------------------------------------- 1 | #ifndef INVERTERS_H 2 | #define INVERTERS_H 3 | 4 | #include "InverterProtocol.h" 5 | extern InverterProtocol* inverter; 6 | 7 | #include "../../USER_SETTINGS.h" 8 | 9 | #include "AFORE-CAN.h" 10 | 11 | #ifdef BYD_CAN_DEYE 12 | #define BYD_CAN 13 | #endif 14 | 15 | #include "BYD-CAN.h" 16 | #include "BYD-MODBUS.h" 17 | #include "FERROAMP-CAN.h" 18 | #include "FOXESS-CAN.h" 19 | #include "GROWATT-HV-CAN.h" 20 | #include "GROWATT-LV-CAN.h" 21 | #include "KOSTAL-RS485.h" 22 | #include "PYLON-CAN.h" 23 | #include "PYLON-LV-CAN.h" 24 | #include "SCHNEIDER-CAN.h" 25 | #include "SMA-BYD-H-CAN.h" 26 | #include "SMA-BYD-HVS-CAN.h" 27 | #include "SMA-LV-CAN.h" 28 | #include "SMA-TRIPOWER-CAN.h" 29 | #include "SOFAR-CAN.h" 30 | #include "SOLAX-CAN.h" 31 | #include "SUNGROW-CAN.h" 32 | 33 | // Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected. 34 | void setup_inverter(); 35 | 36 | #ifdef CAN_INVERTER_SELECTED 37 | void update_values_can_inverter(); 38 | void map_can_frame_to_variable_inverter(CAN_frame rx_frame); 39 | void transmit_can_inverter(unsigned long currentMillis); 40 | #endif 41 | 42 | #ifdef RS485_INVERTER_SELECTED 43 | void receive_RS485(); 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /Software/src/inverter/InverterProtocol.h: -------------------------------------------------------------------------------- 1 | #ifndef INVERTER_PROTOCOL_H 2 | #define INVERTER_PROTOCOL_H 3 | 4 | // The abstract base class for all inverter protocols 5 | class InverterProtocol { 6 | public: 7 | virtual void setup() = 0; 8 | virtual const char* interface_name() = 0; 9 | 10 | // This function maps all the values fetched from battery to the correct battery emulator data structures 11 | virtual void update_values() = 0; 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /Software/src/inverter/ModbusInverterProtocol.cpp: -------------------------------------------------------------------------------- 1 | #include "ModbusInverterProtocol.h" 2 | 3 | static const int MB_RTU_NUM_VALUES = 13100; 4 | uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory 5 | -------------------------------------------------------------------------------- /Software/src/inverter/ModbusInverterProtocol.h: -------------------------------------------------------------------------------- 1 | #ifndef MODBUS_INVERTER_PROTOCOL_H 2 | #define MODBUS_INVERTER_PROTOCOL_H 3 | 4 | #include 5 | #include "../lib/eModbus-eModbus/ModbusServerRTU.h" 6 | #include "HardwareSerial.h" 7 | #include "InverterProtocol.h" 8 | 9 | extern uint16_t mbPV[]; 10 | 11 | // The abstract base class for all Modbus inverter protocols 12 | class ModbusInverterProtocol : public InverterProtocol { 13 | virtual const char* interface_name() { return "RS485 / Modbus"; } 14 | 15 | protected: 16 | // Create a ModbusRTU server instance with 2000ms timeout 17 | ModbusInverterProtocol() : MBserver(2000) { mbPV = ::mbPV; } 18 | 19 | static const int MB_RTU_NUM_VALUES = 13100; 20 | 21 | // Modbus register file 22 | uint16_t* mbPV; 23 | 24 | ModbusServerRTU MBserver; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /Software/src/inverter/PYLON-LV-CAN.h: -------------------------------------------------------------------------------- 1 | #ifndef PYLON_LV_CAN_H 2 | #define PYLON_LV_CAN_H 3 | #include "../include.h" 4 | 5 | #include "CanInverterProtocol.h" 6 | 7 | #ifdef PYLON_LV_CAN 8 | #define CAN_INVERTER_SELECTED 9 | #define SELECTED_INVERTER_CLASS PylonLvInverter 10 | #endif 11 | 12 | class PylonLvInverter : public CanInverterProtocol { 13 | public: 14 | void setup(); 15 | void update_values(); 16 | void transmit_can(unsigned long currentMillis); 17 | void map_can_frame_to_variable(CAN_frame rx_frame); 18 | 19 | private: 20 | void send_system_data(); 21 | void send_setup_info(); 22 | int16_t warning_threshold_of_min(int16_t min_val, int16_t max_val); 23 | 24 | static const int PACK_NUMBER = 0x01; 25 | 26 | // 80 means after reaching 80% of a nominal value a warning is produced (e.g. 80% of max current) 27 | static const int WARNINGS_PERCENT = 80; 28 | 29 | static constexpr const char* MANUFACTURER_NAME = "BatEmuLV"; 30 | 31 | unsigned long previousMillis1000ms = 0; 32 | 33 | CAN_frame PYLON_351 = {.FD = false, 34 | .ext_ID = false, 35 | .DLC = 6, 36 | .ID = 0x351, 37 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 38 | CAN_frame PYLON_355 = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x355, .data = {0x00, 0x00, 0x00, 0x00}}; 39 | CAN_frame PYLON_356 = {.FD = false, 40 | .ext_ID = false, 41 | .DLC = 6, 42 | .ID = 0x356, 43 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 44 | CAN_frame PYLON_359 = {.FD = false, 45 | .ext_ID = false, 46 | .DLC = 7, 47 | .ID = 0x359, 48 | .data = {0x00, 0x00, 0x00, 0x00, PACK_NUMBER, 'P', 'N'}}; 49 | CAN_frame PYLON_35C = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x35C, .data = {0x00, 0x00}}; 50 | CAN_frame PYLON_35E = {.FD = false, 51 | .ext_ID = false, 52 | .DLC = 8, 53 | .ID = 0x35E, 54 | .data = { 55 | MANUFACTURER_NAME[0], 56 | MANUFACTURER_NAME[1], 57 | MANUFACTURER_NAME[2], 58 | MANUFACTURER_NAME[3], 59 | MANUFACTURER_NAME[4], 60 | MANUFACTURER_NAME[5], 61 | MANUFACTURER_NAME[6], 62 | MANUFACTURER_NAME[7], 63 | }}; 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Software/src/inverter/Rs485InverterProtocol.h: -------------------------------------------------------------------------------- 1 | #ifndef RS485CANINVERTER_PROTOCOL_H 2 | #define RS485INVERTER_PROTOCOL_H 3 | 4 | #include "InverterProtocol.h" 5 | 6 | class Rs485InverterProtocol : public InverterProtocol { 7 | public: 8 | virtual const char* interface_name() { return "RS485"; } 9 | virtual void receive_RS485() = 0; 10 | virtual int baud_rate() = 0; 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /Software/src/inverter/SMA-LV-CAN.h: -------------------------------------------------------------------------------- 1 | #ifndef SMA_LV_CAN_H 2 | #define SMA_LV_CAN_H 3 | #include "../include.h" 4 | 5 | #include "CanInverterProtocol.h" 6 | 7 | #ifdef SMA_LV_CAN 8 | #define CAN_INVERTER_SELECTED 9 | #define SELECTED_INVERTER_CLASS SmaLvInverter 10 | #endif 11 | 12 | class SmaLvInverter : public CanInverterProtocol { 13 | public: 14 | void setup(); 15 | void update_values(); 16 | void transmit_can(unsigned long currentMillis); 17 | void map_can_frame_to_variable(CAN_frame rx_frame); 18 | 19 | private: 20 | static const int READY_STATE = 0x03; 21 | static const int STOP_STATE = 0x02; 22 | 23 | unsigned long previousMillis100ms = 0; 24 | 25 | static const int VOLTAGE_OFFSET_DV = 40; //Offset in deciVolt from max charge voltage and min discharge voltage 26 | static const int MAX_VOLTAGE_DV = 630; 27 | static const int MIN_VOLTAGE_DV = 41; 28 | 29 | //Actual content messages 30 | CAN_frame SMA_00F = {.FD = false, // Emergency stop message 31 | .ext_ID = false, 32 | .DLC = 8, //Documentation unclear, should message even have any content? 33 | .ID = 0x00F, 34 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 35 | CAN_frame SMA_351 = {.FD = false, // Battery charge voltage, charge/discharge limit, min discharge voltage 36 | .ext_ID = false, 37 | .DLC = 8, 38 | .ID = 0x351, 39 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 40 | CAN_frame SMA_355 = {.FD = false, // SOC, SOH, HiResSOC 41 | .ext_ID = false, 42 | .DLC = 8, 43 | .ID = 0x355, 44 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 45 | CAN_frame SMA_356 = {.FD = false, // Battery voltage, current, temperature 46 | .ext_ID = false, 47 | .DLC = 8, 48 | .ID = 0x356, 49 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 50 | CAN_frame SMA_35A = {.FD = false, // Alarms & Warnings 51 | .ext_ID = false, 52 | .DLC = 8, 53 | .ID = 0x35A, 54 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 55 | CAN_frame SMA_35B = {.FD = false, // Events 56 | .ext_ID = false, 57 | .DLC = 8, 58 | .ID = 0x35B, 59 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 60 | CAN_frame SMA_35E = {.FD = false, // Manufacturer ASCII 61 | .ext_ID = false, 62 | .DLC = 8, 63 | .ID = 0x35E, 64 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 65 | CAN_frame SMA_35F = {.FD = false, // Battery Type, version, capacity, ID 66 | .ext_ID = false, 67 | .DLC = 8, 68 | .ID = 0x35F, 69 | .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 70 | 71 | int16_t temperature_average = 0; 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /Software/src/inverter/SOFAR-CAN.cpp: -------------------------------------------------------------------------------- 1 | #include "SOFAR-CAN.h" 2 | #include "../communication/can/comm_can.h" 3 | #include "../datalayer/datalayer.h" 4 | #include "../include.h" 5 | 6 | /* This implementation of the SOFAR can protocol is halfway done. What's missing is implementing the inverter replies, all the CAN messages are listed, but the can sending is missing. */ 7 | 8 | void SofarInverter:: 9 | update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages 10 | 11 | //Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage 12 | SOFAR_351.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8); 13 | SOFAR_351.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF); 14 | SOFAR_351.data.u8[2] = (datalayer.battery.status.max_charge_current_dA >> 8); 15 | SOFAR_351.data.u8[3] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); 16 | SOFAR_351.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); 17 | SOFAR_351.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); 18 | //Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage 19 | SOFAR_351.data.u8[6] = (datalayer.battery.info.min_design_voltage_dV >> 8); 20 | SOFAR_351.data.u8[7] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); 21 | 22 | //SOC 23 | SOFAR_355.data.u8[0] = (datalayer.battery.status.reported_soc / 100); 24 | SOFAR_355.data.u8[2] = (datalayer.battery.status.soh_pptt / 100); 25 | //SOFAR_355.data.u8[6] = (AH_remaining >> 8); 26 | //SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF); 27 | 28 | //Voltage (370.0) 29 | SOFAR_356.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); 30 | SOFAR_356.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); 31 | SOFAR_356.data.u8[2] = (datalayer.battery.status.current_dA >> 8); 32 | SOFAR_356.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); 33 | SOFAR_356.data.u8[2] = (datalayer.battery.status.temperature_max_dC >> 8); 34 | SOFAR_356.data.u8[3] = (datalayer.battery.status.temperature_max_dC & 0x00FF); 35 | } 36 | 37 | void SofarInverter::map_can_frame_to_variable(CAN_frame rx_frame) { 38 | switch (rx_frame.ID) { //In here we need to respond to the inverter. TODO: make logic 39 | case 0x605: 40 | datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; 41 | //frame1_605 = rx_frame.data.u8[1]; 42 | //frame3_605 = rx_frame.data.u8[3]; 43 | break; 44 | case 0x705: 45 | datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; 46 | //frame1_705 = rx_frame.data.u8[1]; 47 | //frame3_705 = rx_frame.data.u8[3]; 48 | break; 49 | default: 50 | break; 51 | } 52 | } 53 | 54 | void SofarInverter::transmit_can(unsigned long currentMillis) { 55 | // Send 100ms CAN Message 56 | if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { 57 | previousMillis100 = currentMillis; 58 | //Frames actively reported by BMS 59 | transmit_can_frame(&SOFAR_351, can_config.inverter); 60 | transmit_can_frame(&SOFAR_355, can_config.inverter); 61 | transmit_can_frame(&SOFAR_356, can_config.inverter); 62 | transmit_can_frame(&SOFAR_30F, can_config.inverter); 63 | transmit_can_frame(&SOFAR_359, can_config.inverter); 64 | transmit_can_frame(&SOFAR_35E, can_config.inverter); 65 | transmit_can_frame(&SOFAR_35F, can_config.inverter); 66 | transmit_can_frame(&SOFAR_35A, can_config.inverter); 67 | } 68 | } 69 | 70 | void SofarInverter::setup(void) { // Performs one time setup at startup over CAN bus 71 | strncpy(datalayer.system.info.inverter_protocol, "Sofar BMS (Extended Frame) over CAN bus", 63); 72 | datalayer.system.info.inverter_protocol[63] = '\0'; 73 | } 74 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCDIRS 2 | "src" 3 | ) 4 | 5 | set(COMPONENT_ADD_INCLUDEDIRS 6 | "src" 7 | ) 8 | 9 | register_component() 10 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESPAsyncWebServer", 3 | "version": "3.7.2", 4 | "description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", 5 | "keywords": "http,async,websocket,webserver", 6 | "homepage": "https://github.com/ESP32Async/ESPAsyncWebServer", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/ESP32Async/ESPAsyncWebServer.git" 10 | }, 11 | "authors": 12 | { 13 | "name": "ESP32Async", 14 | "maintainer": true 15 | }, 16 | "license": "LGPL-3.0", 17 | "frameworks": "arduino", 18 | "platforms": [ 19 | "espressif32", 20 | "espressif8266", 21 | "raspberrypi" 22 | ], 23 | "dependencies": [ 24 | { 25 | "owner": "ESP32Async", 26 | "name": "AsyncTCP", 27 | "version": "^3.3.6", 28 | "platforms": "espressif32" 29 | }, 30 | { 31 | "owner": "ESP32Async", 32 | "name": "ESPAsyncTCP", 33 | "version": "^2.0.0", 34 | "platforms": "espressif8266" 35 | }, 36 | { 37 | "name": "Hash", 38 | "platforms": "espressif8266" 39 | }, 40 | { 41 | "owner": "ayushsharma82", 42 | "name": "RPAsyncTCP", 43 | "version": "^1.3.1", 44 | "platforms": "raspberrypi" 45 | } 46 | ], 47 | "export": { 48 | "include": [ 49 | "examples", 50 | "src", 51 | "library.json", 52 | "library.properties", 53 | "LICENSE", 54 | "README.md" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/library.properties: -------------------------------------------------------------------------------- 1 | name=ESP Async WebServer 2 | includes=ESPAsyncWebServer.h 3 | version=3.7.2 4 | author=ESP32Async 5 | maintainer=ESP32Async 6 | sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 7 | paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc 8 | category=Other 9 | url=https://github.com/ESP32Async/ESPAsyncWebServer 10 | architectures=* 11 | license=LGPL-3.0 12 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/AsyncWebHeader.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #include "ESPAsyncWebServer.h" 5 | 6 | AsyncWebHeader::AsyncWebHeader(const String &data) { 7 | if (!data) { 8 | return; 9 | } 10 | int index = data.indexOf(':'); 11 | if (index < 0) { 12 | return; 13 | } 14 | _name = data.substring(0, index); 15 | _value = data.substring(index + 2); 16 | } 17 | 18 | String AsyncWebHeader::toString() const { 19 | String str; 20 | if (str.reserve(_name.length() + _value.length() + 2)) { 21 | str.concat(_name); 22 | str.concat((char)0x3a); 23 | str.concat((char)0x20); 24 | str.concat(_value); 25 | str.concat(asyncsrv::T_rn); 26 | } else { 27 | #ifdef ESP32 28 | log_e("Failed to allocate"); 29 | #endif 30 | } 31 | return str; 32 | } 33 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/AsyncWebServerVersion.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #pragma once 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** Major version number (X.x.x) */ 11 | #define ASYNCWEBSERVER_VERSION_MAJOR 3 12 | /** Minor version number (x.X.x) */ 13 | #define ASYNCWEBSERVER_VERSION_MINOR 7 14 | /** Patch version number (x.x.X) */ 15 | #define ASYNCWEBSERVER_VERSION_PATCH 2 16 | 17 | /** 18 | * Macro to convert version number into an integer 19 | * 20 | * To be used in comparisons, such as ASYNCWEBSERVER_VERSION >= ASYNCWEBSERVER_VERSION_VAL(2, 0, 0) 21 | */ 22 | #define ASYNCWEBSERVER_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) 23 | 24 | /** 25 | * Current version, as an integer 26 | * 27 | * To be used in comparisons, such as ASYNCWEBSERVER_VERSION_NUM >= ASYNCWEBSERVER_VERSION_VAL(2, 0, 0) 28 | */ 29 | #define ASYNCWEBSERVER_VERSION_NUM ASYNCWEBSERVER_VERSION_VAL(ASYNCWEBSERVER_VERSION_MAJOR, ASYNCWEBSERVER_VERSION_MINOR, ASYNCWEBSERVER_VERSION_PATCH) 30 | 31 | /** 32 | * Current version, as string 33 | */ 34 | #define df2xstr(s) #s 35 | #define df2str(s) df2xstr(s) 36 | #define ASYNCWEBSERVER_VERSION df2str(ASYNCWEBSERVER_VERSION_MAJOR) "." df2str(ASYNCWEBSERVER_VERSION_MINOR) "." df2str(ASYNCWEBSERVER_VERSION_PATCH) 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #if ESP_IDF_VERSION_MAJOR < 5 17 | 18 | #ifndef SHA1Builder_h 19 | #define SHA1Builder_h 20 | 21 | #include 22 | #include 23 | 24 | #define SHA1_HASH_SIZE 20 25 | 26 | class SHA1Builder { 27 | private: 28 | uint32_t total[2]; /* number of bytes processed */ 29 | uint32_t state[5]; /* intermediate digest state */ 30 | unsigned char buffer[64]; /* data block being processed */ 31 | uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ 32 | 33 | void process(const uint8_t *data); 34 | 35 | public: 36 | void begin(); 37 | void add(const uint8_t *data, size_t len); 38 | void calculate(); 39 | void getBytes(uint8_t *output); 40 | }; 41 | 42 | #endif // SHA1Builder_h 43 | 44 | #endif // ESP_IDF_VERSION_MAJOR < 5 45 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/ChunkPrint.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #include "ChunkPrint.h" 5 | 6 | ChunkPrint::ChunkPrint(uint8_t *destination, size_t from, size_t len) : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} 7 | 8 | size_t ChunkPrint::write(uint8_t c) { 9 | if (_to_skip > 0) { 10 | _to_skip--; 11 | return 1; 12 | } else if (_to_write > 0) { 13 | _to_write--; 14 | _destination[_pos++] = c; 15 | return 1; 16 | } 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/ChunkPrint.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #ifndef CHUNKPRINT_H 5 | #define CHUNKPRINT_H 6 | 7 | #include 8 | 9 | class ChunkPrint : public Print { 10 | private: 11 | uint8_t *_destination; 12 | size_t _to_skip; 13 | size_t _to_write; 14 | size_t _pos; 15 | 16 | public: 17 | ChunkPrint(uint8_t *destination, size_t from, size_t len); 18 | size_t write(uint8_t c); 19 | size_t write(const uint8_t *buffer, size_t size) { 20 | return this->Print::write(buffer, size); 21 | } 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/WebAuthentication.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #ifndef WEB_AUTHENTICATION_H_ 5 | #define WEB_AUTHENTICATION_H_ 6 | 7 | #include "Arduino.h" 8 | 9 | bool checkBasicAuthentication(const char *header, const char *username, const char *password); 10 | 11 | bool checkDigestAuthentication( 12 | const char *header, const char *method, const char *username, const char *password, const char *realm, bool passwordIsHash, const char *nonce, 13 | const char *opaque, const char *uri 14 | ); 15 | 16 | // for storing hashed versions on the device that can be authenticated against 17 | String generateDigestHash(const char *username, const char *password, const char *realm); 18 | 19 | String generateBasicHash(const char *username, const char *password); 20 | 21 | String genRandomMD5(); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Software/src/lib/ESP32Async-ESPAsyncWebServer/src/WebHandlerImpl.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-or-later 2 | // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov 3 | 4 | #ifndef ASYNCWEBSERVERHANDLERIMPL_H_ 5 | #define ASYNCWEBSERVERHANDLERIMPL_H_ 6 | 7 | #include 8 | #ifdef ASYNCWEBSERVER_REGEX 9 | #include 10 | #endif 11 | 12 | #include "stddef.h" 13 | #include 14 | 15 | class AsyncStaticWebHandler : public AsyncWebHandler { 16 | using File = fs::File; 17 | using FS = fs::FS; 18 | 19 | private: 20 | bool _getFile(AsyncWebServerRequest *request) const; 21 | bool _searchFile(AsyncWebServerRequest *request, const String &path); 22 | uint8_t _countBits(const uint8_t value) const; 23 | 24 | protected: 25 | FS _fs; 26 | String _uri; 27 | String _path; 28 | String _default_file; 29 | String _cache_control; 30 | String _last_modified; 31 | AwsTemplateProcessor _callback; 32 | bool _isDir; 33 | bool _tryGzipFirst = true; 34 | 35 | public: 36 | AsyncStaticWebHandler(const char *uri, FS &fs, const char *path, const char *cache_control); 37 | bool canHandle(AsyncWebServerRequest *request) const override final; 38 | void handleRequest(AsyncWebServerRequest *request) override final; 39 | AsyncStaticWebHandler &setTryGzipFirst(bool value); 40 | AsyncStaticWebHandler &setIsDir(bool isDir); 41 | AsyncStaticWebHandler &setDefaultFile(const char *filename); 42 | AsyncStaticWebHandler &setCacheControl(const char *cache_control); 43 | 44 | /** 45 | * @brief Set the Last-Modified time for the object 46 | * 47 | * @param last_modified 48 | * @return AsyncStaticWebHandler& 49 | */ 50 | AsyncStaticWebHandler &setLastModified(const char *last_modified); 51 | AsyncStaticWebHandler &setLastModified(struct tm *last_modified); 52 | AsyncStaticWebHandler &setLastModified(time_t last_modified); 53 | // sets to current time. Make sure sntp is running and time is updated 54 | AsyncStaticWebHandler &setLastModified(); 55 | 56 | AsyncStaticWebHandler &setTemplateProcessor(AwsTemplateProcessor newCallback); 57 | }; 58 | 59 | class AsyncCallbackWebHandler : public AsyncWebHandler { 60 | private: 61 | protected: 62 | String _uri; 63 | WebRequestMethodComposite _method; 64 | ArRequestHandlerFunction _onRequest; 65 | ArUploadHandlerFunction _onUpload; 66 | ArBodyHandlerFunction _onBody; 67 | bool _isRegex; 68 | 69 | public: 70 | AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} 71 | void setUri(const String &uri); 72 | void setMethod(WebRequestMethodComposite method) { 73 | _method = method; 74 | } 75 | void onRequest(ArRequestHandlerFunction fn) { 76 | _onRequest = fn; 77 | } 78 | void onUpload(ArUploadHandlerFunction fn) { 79 | _onUpload = fn; 80 | } 81 | void onBody(ArBodyHandlerFunction fn) { 82 | _onBody = fn; 83 | } 84 | 85 | bool canHandle(AsyncWebServerRequest *request) const override final; 86 | void handleRequest(AsyncWebServerRequest *request) override final; 87 | void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) override final; 88 | void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final; 89 | bool isRequestHandlerTrivial() const override final { 90 | return !_onRequest; 91 | } 92 | }; 93 | 94 | #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ 95 | -------------------------------------------------------------------------------- /Software/src/lib/YiannisBourkelis-Uptime-Library/README.md: -------------------------------------------------------------------------------- 1 | # Uptime Library 2 | 3 | With the uptime library for Arduino boards and compatible systems you can read the time passed since device startup, without the 49 days overflow limitation of the millis() function. 4 | 5 | # Usage 6 | 7 | #### Example 1: [Device Uptime](https://github.com/YiannisBourkelis/Uptime-Library/tree/master/examples/DeviceUptime "Device Uptime") 8 | ```cpp 9 | #include "uptime_formatter.h" 10 | 11 | void setup() { 12 | // connect at 115200 so we can read the uptime fast enough 13 | Serial.begin(115200); 14 | } 15 | 16 | void loop() { 17 | //uptime_formatter::get_uptime() returns a string 18 | //containing the total device uptime since startup in days, hours, minutes and seconds 19 | Serial.println("up " + uptime_formatter::getUptime()); 20 | 21 | //wait 1 second 22 | delay(1000); 23 | } 24 | ``` 25 | 26 | #### Output: 27 | ``` 28 | up 0 days, 0 hours, 0 minutes, 56 seconds 29 | up 0 days, 0 hours, 0 minutes, 57 seconds 30 | up 0 days, 0 hours, 0 minutes, 58 seconds 31 | up 0 days, 0 hours, 0 minutes, 59 seconds 32 | up 0 days, 0 hours, 1 minutes, 0 seconds 33 | up 0 days, 0 hours, 1 minutes, 1 seconds 34 | up 0 days, 0 hours, 1 minutes, 2 seconds 35 | up 0 days, 0 hours, 1 minutes, 3 seconds 36 | up 0 days, 0 hours, 1 minutes, 4 seconds 37 | up 0 days, 0 hours, 1 minutes, 5 seconds 38 | up 0 days, 0 hours, 1 minutes, 6 seconds 39 | up 0 days, 0 hours, 1 minutes, 7 seconds 40 | up 0 days, 0 hours, 1 minutes, 8 seconds 41 | up 0 days, 0 hours, 1 minutes, 9 seconds 42 | ``` 43 | 44 | #### Example 2: [Device Uptime Custom Formatting](https://github.com/YiannisBourkelis/Uptime-Library/tree/master/examples/DeviceUptimeCustomFormatting "Device Uptime Custom Formatting") 45 | ```cpp 46 | #include "uptime.h" 47 | 48 | void setup() { 49 | // connect at 115200 so we can read the uptime fast enough 50 | Serial.begin(115200); 51 | } 52 | 53 | void loop() { 54 | //If you do not want to use the string library 55 | //you can get the total uptime variables 56 | //and format the output the way you want. 57 | 58 | //First call calculate_uptime() to calculate the uptime 59 | //and then read the uptime variables. 60 | uptime::calculateUptime(); 61 | 62 | Serial.print("days: "); 63 | Serial.println(uptime::getDays()); 64 | 65 | Serial.print("hours: "); 66 | Serial.println(uptime::getHours()); 67 | 68 | Serial.print("minutes: "); 69 | Serial.println(uptime::getMinutes()); 70 | 71 | Serial.print("seconds: "); 72 | Serial.println(uptime::getSeconds()); 73 | 74 | Serial.print("milliseconds: "); 75 | Serial.println(uptime::getMilliseconds()); 76 | 77 | Serial.print("\n"); 78 | 79 | //wait 1 second 80 | delay(1000); 81 | } 82 | ``` 83 | 84 | #### Output: 85 | ``` 86 | days: 0 87 | hours: 0 88 | minutes: 23 89 | seconds: 16 90 | milliseconds: 23 91 | 92 | days: 0 93 | hours: 0 94 | minutes: 23 95 | seconds: 17 96 | milliseconds: 23 97 | 98 | days: 0 99 | hours: 0 100 | minutes: 23 101 | seconds: 18 102 | milliseconds: 23 103 | ``` 104 | -------------------------------------------------------------------------------- /Software/src/lib/YiannisBourkelis-Uptime-Library/library.properties: -------------------------------------------------------------------------------- 1 | name=Uptime Library 2 | version=1.0.0 3 | author=Yiannis Bourkelis 4 | maintainer=Yiannis Bourkelis 5 | sentence=Uptime library for Arduino boards and compatible systems 6 | paragraph=Easily read the uptime since device startup, in days, hours, minutes and milliseconds, without the 49 days overflow limitation of the millis() function. 7 | category=Timing 8 | url=https://github.com/YiannisBourkelis/Uptime-Library 9 | architectures=* 10 | -------------------------------------------------------------------------------- /Software/src/lib/YiannisBourkelis-Uptime-Library/src/uptime.h: -------------------------------------------------------------------------------- 1 | /* *********************************************************************** 2 | * Uptime library for Arduino boards and compatible systems 3 | * (C) 2019 by Yiannis Bourkelis (https://github.com/YiannisBourkelis/) 4 | * 5 | * This file is part of Uptime library for Arduino boards and compatible systems 6 | * 7 | * Uptime library for Arduino boards and compatible systems is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * Uptime library for Arduino boards and compatible systems is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with Uptime library for Arduino boards and compatible systems. If not, see . 19 | * ***********************************************************************/ 20 | #ifndef UPTIMELIB_H 21 | #define UPTIMELIB_H 22 | /* 23 | * Uptime library for Arduino devices 24 | * 25 | * Caclulates the time passed since the device boot time, even after the millis() overflow, after 49 days 26 | * 27 | * Usage: 28 | * include "uptime_formatter.h" 29 | * Inside your loop() function: 30 | * Serial.println("Uptime: " + uptime_formatter::get_uptime()); 31 | * 32 | * Examples here: 33 | * 34 | * Created 08 May 2019 35 | * By Yiannis Bourkelis 36 | * 37 | * https://github.com/YiannisBourkelis/ 38 | * 39 | *Complete documentation for each function and variable name exist 40 | *inside the implementation uptime.cpp file 41 | */ 42 | 43 | class uptime 44 | { 45 | public: 46 | uptime(); 47 | 48 | static void calculateUptime(); 49 | 50 | static unsigned long getSeconds(); 51 | static unsigned long getMinutes(); 52 | static unsigned long getHours(); 53 | static unsigned long getDays(); 54 | 55 | private: 56 | static unsigned long m_milliseconds; 57 | static unsigned long m_seconds; 58 | static unsigned long m_minutes; 59 | static unsigned long m_hours; 60 | static unsigned long m_days; 61 | 62 | static unsigned long m_mod_milliseconds; 63 | static uint8_t m_mod_seconds; 64 | static uint8_t m_mod_minutes; 65 | static uint8_t m_mod_hours; 66 | 67 | static unsigned long m_last_milliseconds; 68 | static unsigned long m_remaining_seconds; 69 | static unsigned long m_remaining_minutes; 70 | static unsigned long m_remaining_hours; 71 | static unsigned long m_remaining_days; 72 | }; 73 | #endif 74 | -------------------------------------------------------------------------------- /Software/src/lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.cpp: -------------------------------------------------------------------------------- 1 | /* *********************************************************************** 2 | * Uptime library for Arduino boards and compatible systems 3 | * (C) 2019 by Yiannis Bourkelis (https://github.com/YiannisBourkelis/) 4 | * 5 | * This file is part of Uptime library for Arduino boards and compatible systems 6 | * 7 | * Uptime library for Arduino boards and compatible systems is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * Uptime library for Arduino boards and compatible systems is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with Uptime library for Arduino boards and compatible systems. If not, see . 19 | * ***********************************************************************/ 20 | 21 | #include "uptime_formatter.h" 22 | #include "uptime.h" 23 | 24 | uptime_formatter::uptime_formatter() 25 | { 26 | } 27 | 28 | //returns the actual time passed since device boot 29 | //in the format: x days, y hours, z minutes, s seconds 30 | String uptime_formatter::getUptime() 31 | { 32 | uptime::calculateUptime(); 33 | 34 | return (String)(uptime::getDays() ) + " days, " + 35 | (String)(uptime::getHours() ) + " hours, " + 36 | (String)(uptime::getMinutes()) + " minutes, " + 37 | (String)(uptime::getSeconds()) + " seconds"; 38 | } 39 | -------------------------------------------------------------------------------- /Software/src/lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h: -------------------------------------------------------------------------------- 1 | /* *********************************************************************** 2 | * Uptime library for Arduino boards and compatible systems 3 | * (C) 2019 by Yiannis Bourkelis (https://github.com/YiannisBourkelis/) 4 | * 5 | * This file is part of Uptime library for Arduino boards and compatible systems 6 | * 7 | * Uptime library for Arduino boards and compatible systems is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * Uptime library for Arduino boards and compatible systems is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with Uptime library for Arduino boards and compatible systems. If not, see . 19 | * *********************************************************************** 20 | * Complete documentation for each function name exist 21 | * inside the implementation uptime_formatter.cpp file 22 | */ 23 | #ifndef UPTIMELIBF_H 24 | #define UPTIMELIBF_H 25 | 26 | #include "WString.h" 27 | 28 | class uptime_formatter 29 | { 30 | public: 31 | uptime_formatter(); 32 | 33 | static String getUptime(); 34 | }; 35 | #endif 36 | -------------------------------------------------------------------------------- /Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c: -------------------------------------------------------------------------------- 1 | // Implements the RMT peripheral on Espressif SoCs 2 | // Copyright (c) 2020 Lucian Copeland for Adafruit Industries 3 | 4 | /* Uses code from Espressif RGB LED Strip demo and drivers 5 | * Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | #include 21 | #include "driver/rmt.h" 22 | 23 | // This code is adapted from the ESP-IDF v3.4 RMT "led_strip" example, altered 24 | // to work with the Arduino version of the ESP-IDF (3.2) 25 | 26 | #define WS2812_T0H_NS (400) 27 | #define WS2812_T0L_NS (850) 28 | #define WS2812_T1H_NS (800) 29 | #define WS2812_T1L_NS (450) 30 | 31 | static uint32_t t0h_ticks = 0; 32 | static uint32_t t1h_ticks = 0; 33 | static uint32_t t0l_ticks = 0; 34 | static uint32_t t1l_ticks = 0; 35 | 36 | static void IRAM_ATTR ws2812_rmt_adapter(const void* src, rmt_item32_t* dest, size_t src_size, size_t wanted_num, 37 | size_t* translated_size, size_t* item_num) { 38 | if (src == NULL || dest == NULL) { 39 | *translated_size = 0; 40 | *item_num = 0; 41 | return; 42 | } 43 | const rmt_item32_t bit0 = {{{t0h_ticks, 1, t0l_ticks, 0}}}; //Logical 0 44 | const rmt_item32_t bit1 = {{{t1h_ticks, 1, t1l_ticks, 0}}}; //Logical 1 45 | size_t size = 0; 46 | size_t num = 0; 47 | uint8_t* psrc = (uint8_t*)src; 48 | rmt_item32_t* pdest = dest; 49 | while (size < src_size && num < wanted_num) { 50 | for (int i = 0; i < 8; i++) { 51 | // MSB first 52 | if (*psrc & (1 << (7 - i))) { 53 | pdest->val = bit1.val; 54 | } else { 55 | pdest->val = bit0.val; 56 | } 57 | num++; 58 | pdest++; 59 | } 60 | size++; 61 | psrc++; 62 | } 63 | *translated_size = size; 64 | *item_num = num; 65 | } 66 | 67 | static bool rmt_initialized = false; 68 | 69 | void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { 70 | if (rmt_initialized == false) { 71 | // Reserve channel 72 | rmt_channel_t channel = 0; 73 | 74 | rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel); 75 | config.clk_div = 2; 76 | 77 | rmt_config(&config); 78 | rmt_driver_install(config.channel, 0, 0); 79 | 80 | // Convert NS timings to ticks 81 | uint32_t counter_clk_hz = 0; 82 | 83 | rmt_get_counter_clock(channel, &counter_clk_hz); 84 | 85 | // NS to tick converter 86 | float ratio = (float)counter_clk_hz / 1e9; 87 | 88 | t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); 89 | t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); 90 | t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); 91 | t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); 92 | 93 | // Initialize automatic timing translator 94 | rmt_translator_init(0, ws2812_rmt_adapter); 95 | rmt_initialized = true; 96 | } 97 | 98 | // Write and wait to finish 99 | rmt_write_sample(0, pixels, (size_t)numBytes, false); 100 | } 101 | -------------------------------------------------------------------------------- /Software/src/lib/ayushsharma82-ElegantOTA/keywords.txt: -------------------------------------------------------------------------------- 1 | ElegantOTA KEYWORD1 2 | begin KEYWORD2 3 | loop KEYWORD2 4 | setAuth KEYWORD2 5 | clearAuth KEYWORD2 6 | setAutoReboot KEYWORD2 7 | onStart KEYWORD2 8 | onEnd KEYWORD2 9 | onProgress KEYWORD2 10 | -------------------------------------------------------------------------------- /Software/src/lib/ayushsharma82-ElegantOTA/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ElegantOTA", 3 | "keywords": "ElegantOTA, OTA, Update, ESP8266, ESP32, over, the, air, firmware, filesystem, littlefs, spiffs", 4 | "description": "OTA updates made slick and simple for everyone!", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/ayushsharma82/ElegantOTA.git" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Ayush Sharma", 14 | "email": "asrocks5@gmail.com", 15 | "maintainer": true 16 | } 17 | ], 18 | "dependencies": [ 19 | { 20 | "owner": "mathieucarbou", 21 | "name": "ESPAsyncWebServer", 22 | "version": "^3.3.11", 23 | "platforms": ["espressif8266", "espressif32"] 24 | } 25 | ], 26 | "version": "3.1.6", 27 | "frameworks": "arduino", 28 | "platforms": ["espressif8266", "espressif32", "raspberrypi"], 29 | "build": { 30 | "libCompatMode": "strict" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Software/src/lib/ayushsharma82-ElegantOTA/library.properties: -------------------------------------------------------------------------------- 1 | name=ElegantOTA 2 | version=3.1.6 3 | author=Ayush Sharma 4 | category=Communication 5 | maintainer=Ayush Sharma 6 | sentence=OTA updates made slick and simple for everyone! 7 | paragraph=A OTA library which provides an interactive portal for your over-the-air updates for wireless microcontrollers. 8 | url=https://github.com/ayushsharma82/ElegantOTA 9 | architectures=esp8266,esp32,rp2040 10 | -------------------------------------------------------------------------------- /Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h: -------------------------------------------------------------------------------- 1 | #ifndef elop_h 2 | #define elop_h 3 | 4 | #include 5 | 6 | extern const uint8_t ELEGANT_HTML[41354]; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/Logging.cpp: -------------------------------------------------------------------------------- 1 | // ================================================================================================= 2 | // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus 3 | // MIT license - see license.md for details 4 | // ================================================================================================= 5 | #include "Logging.h" 6 | #include 7 | 8 | int MBUlogLvl = LOG_LEVEL; 9 | #if IS_LINUX 10 | #define PrintOut printf 11 | 12 | void logHexDump(const char *letter, const char *label, const uint8_t *data, const size_t length) { 13 | #else 14 | Print *LOGDEVICE = &Serial; 15 | #define PrintOut output->printf 16 | 17 | void logHexDump(Print *output, const char *letter, const char *label, const uint8_t *data, const size_t length) { 18 | #endif 19 | size_t cnt = 0; 20 | size_t step = 0; 21 | char limiter = '|'; 22 | // Use line buffer to speed up output 23 | const uint16_t BUFLEN(80); 24 | const uint16_t ascOffset(61); 25 | char linebuf[BUFLEN]; 26 | char *cp = linebuf; 27 | const char HEXDIGIT[] = "0123456789ABCDEF"; 28 | 29 | // Print out header 30 | PrintOut ("[%s] %s: @%" PRIXPTR "/%" PRIu32 ":\n", letter, label, (uintptr_t)data, (uint32_t)(length & 0xFFFFFFFF)); 31 | 32 | // loop over data in steps of 16 33 | for (cnt = 0; cnt < length; ++cnt) { 34 | step = cnt % 16; 35 | // New line? 36 | if (step == 0) { 37 | // Yes. Clear line and print address header 38 | memset(linebuf, ' ', BUFLEN); 39 | linebuf[60] = limiter; 40 | linebuf[77] = limiter; 41 | linebuf[78] = '\n'; 42 | linebuf[BUFLEN - 1] = 0; 43 | snprintf(linebuf, BUFLEN, " %c %04X: ", limiter, (uint16_t)(cnt & 0xFFFF)); 44 | cp = linebuf + strlen(linebuf); 45 | // No, but first block of 8 done? 46 | } else if (step == 8) { 47 | // Yes, put out additional space 48 | cp++; 49 | } 50 | // Print data byte 51 | uint8_t c = data[cnt]; 52 | *cp++ = HEXDIGIT[(c >> 4) & 0x0F]; 53 | *cp++ = HEXDIGIT[c & 0x0F]; 54 | *cp++ = ' '; 55 | if (c >= 32 && c <= 127) linebuf[ascOffset + step] = c; 56 | else linebuf[ascOffset + step] = '.'; 57 | // Line end? 58 | if (step == 15) { 59 | // Yes, print line 60 | PrintOut ("%s", linebuf); 61 | } 62 | } 63 | // Unfinished line? 64 | if (length && step != 15) { 65 | // Yes, print line 66 | PrintOut ("%s", linebuf); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/ModbusServer.h: -------------------------------------------------------------------------------- 1 | // ================================================================================================= 2 | // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus 3 | // MIT license - see license.md for details 4 | // ================================================================================================= 5 | #ifndef _MODBUS_SERVER_H 6 | #define _MODBUS_SERVER_H 7 | 8 | #include "options.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #if USE_MUTEX 14 | #include // NOLINT 15 | #endif 16 | #include "ModbusTypeDefs.h" 17 | #include "ModbusError.h" 18 | #include "ModbusMessage.h" 19 | 20 | #if USE_MUTEX 21 | using std::mutex; 22 | using std::lock_guard; 23 | #endif 24 | 25 | // Standard response variants for "no response" and "echo the request" 26 | const ModbusMessage NIL_RESPONSE (std::vector{0xFF, 0xF0}); 27 | const ModbusMessage ECHO_RESPONSE(std::vector{0xFF, 0xF1}); 28 | 29 | // MBSworker: function signature for worker functions to handle single serverID/functionCode combinations 30 | using MBSworker = std::function; 31 | 32 | class ModbusServer { 33 | public: 34 | // registerWorker: register a worker function for a certain serverID/FC combination 35 | // If there is one already, it will be overwritten! 36 | void registerWorker(uint8_t serverID, uint8_t functionCode, MBSworker worker); 37 | 38 | // getWorker: if a worker function is registered, return its address, nullptr otherwise 39 | MBSworker getWorker(uint8_t serverID, uint8_t functionCode); 40 | 41 | // unregisterWorker; remove again all or part of the registered workers for a given server ID 42 | // Returns true if the worker was found and removed 43 | bool unregisterWorker(uint8_t serverID, uint8_t functionCode = 0); 44 | 45 | // isServerFor: if a worker function is registered for the given serverID, return true 46 | bool isServerFor(uint8_t serverID, uint8_t functionCode); 47 | 48 | // isServerFor: short version to look up if the server is known at all 49 | bool isServerFor(uint8_t serverID); 50 | 51 | // getMessageCount: read number of messages processed 52 | uint32_t getMessageCount(); 53 | 54 | // getErrorCount: read number of errors responded 55 | uint32_t getErrorCount(); 56 | 57 | // resetCounts: set both message and error counts to zero 58 | void resetCounts(); 59 | 60 | // Local request to the server 61 | ModbusMessage localRequest(ModbusMessage msg); 62 | 63 | // listServer: print out all server/FC combinations served 64 | void listServer(); 65 | 66 | protected: 67 | // Constructor 68 | ModbusServer(); 69 | 70 | // Destructor 71 | ~ModbusServer(); 72 | 73 | // Prevent copy construction or assignment 74 | ModbusServer(ModbusServer& other) = delete; 75 | ModbusServer& operator=(ModbusServer& other) = delete; 76 | 77 | // Virtual function to prevent this class being instantiated 78 | virtual void isInstance() = 0; 79 | 80 | std::map> workerMap; // map on serverID->functionCode->worker function 81 | uint32_t messageCount; // Number of Requests processed 82 | uint32_t errorCount; // Number of errors responded 83 | #if USE_MUTEX 84 | mutex m; // mutex to cover changes to messageCount and errorCount 85 | #endif 86 | }; 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h: -------------------------------------------------------------------------------- 1 | // ================================================================================================= 2 | // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus 3 | // MIT license - see license.md for details 4 | // ================================================================================================= 5 | #ifndef _MODBUS_SERVER_ETHERNET_H 6 | #define _MODBUS_SERVER_ETHERNET_H 7 | #include "options.h" 8 | #if HAS_ETHERNET == 1 9 | #include 10 | #include 11 | 12 | #undef SERVER_END 13 | #define SERVER_END // NIL for Ethernet 14 | 15 | #include "ModbusServerTCPtemp.h" 16 | using ModbusServerEthernet = ModbusServerTCP; 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/RTUutils.h: -------------------------------------------------------------------------------- 1 | // ================================================================================================= 2 | // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus 3 | // MIT license - see license.md for details 4 | // ================================================================================================= 5 | #ifndef _RTU_UTILS_H 6 | #define _RTU_UTILS_H 7 | #include 8 | #if NEED_UART_PATCH 9 | #include 10 | #endif 11 | #include 12 | #include "Stream.h" 13 | #include "ModbusTypeDefs.h" 14 | #include 15 | 16 | typedef std::function RTScallback; 17 | 18 | using namespace Modbus; // NOLINT 19 | 20 | // RTUutils is bundling the send, receive and CRC functions for Modbus RTU communications. 21 | // RTU server and client will make use of it. 22 | // All functions are static! 23 | class RTUutils { 24 | public: 25 | friend class ModbusClientRTU; 26 | friend class ModbusServerRTU; 27 | 28 | // calcCRC: calculate the CRC16 value for a given block of data 29 | static uint16_t calcCRC(const uint8_t *data, uint16_t len); 30 | 31 | // calcCRC: calculate the CRC16 value for a given block of data 32 | static uint16_t calcCRC(ModbusMessage msg); 33 | 34 | // validCRC #1: check the CRC in a block of data for validity 35 | static bool validCRC(const uint8_t *data, uint16_t len); 36 | 37 | // validCRC #2: check the CRC of a block of data against a given one 38 | static bool validCRC(const uint8_t *data, uint16_t len, uint16_t CRC); 39 | 40 | // validCRC #1: check the CRC in a message for validity 41 | static bool validCRC(ModbusMessage msg); 42 | 43 | // validCRC #2: check the CRC of a message against a given one 44 | static bool validCRC(ModbusMessage msg, uint16_t CRC); 45 | 46 | // addCRC: extend a RTUMessage by a valid CRC 47 | static void addCRC(ModbusMessage& raw); 48 | 49 | // calculateInterval: determine the minimal gap time between messages 50 | static uint32_t calculateInterval(uint32_t baudRate); 51 | 52 | // RTSauto: dummy callback for auto half duplex RS485 boards 53 | inline static void RTSauto(bool level) { return; } // NOLINT 54 | 55 | // Necessary preparations for a HardwareSerial 56 | static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) { 57 | s.setRxBufferSize(bufferSize); 58 | s.setTxBufferSize(bufferSize); 59 | } 60 | 61 | protected: 62 | // Printable characters for ASCII protocol: 012345678ABCDEF 63 | static const char ASCIIwrite[]; 64 | static const char ASCIIread[]; 65 | 66 | RTUutils() = delete; 67 | 68 | // receive: get a Modbus message from serial, maintaining timeouts etc. 69 | static ModbusMessage receive(uint8_t caller, Stream& serial, uint32_t timeout, unsigned long& lastMicros, uint32_t interval, bool ASCIImode, bool skipLeadingZeroBytes = false); 70 | 71 | // send: send a Modbus message in either format (ModbusMessage or data/len) 72 | static void send(Stream& serial, unsigned long& lastMicros, uint32_t interval, RTScallback r, const uint8_t *data, uint16_t len, bool ASCIImode); 73 | static void send(Stream& serial, unsigned long& lastMicros, uint32_t interval, RTScallback r, ModbusMessage raw, bool ASCIImode); 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/options.h: -------------------------------------------------------------------------------- 1 | // ================================================================================================= 2 | // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus 3 | // MIT license - see license.md for details 4 | // ================================================================================================= 5 | #ifndef _EMODBUS_OPTIONS_H 6 | #define _EMODBUS_OPTIONS_H 7 | 8 | /* === ESP32 DEFINITIONS AND MACROS === */ 9 | #if defined(ESP32) 10 | #include 11 | #define USE_MUTEX 1 12 | #define HAS_FREERTOS 1 13 | #define HAS_ETHERNET 1 14 | #define IS_LINUX 0 15 | #define NEED_UART_PATCH 1 16 | const unsigned int SERVER_TASK_STACK = 4096; 17 | const unsigned int CLIENT_TASK_STACK = 4096; 18 | 19 | /* === ESP8266 DEFINITIONS AND MACROS === */ 20 | #elif defined(ESP8266) 21 | #include 22 | #define USE_MUTEX 0 23 | #define HAS_FREERTOS 0 24 | #define HAS_ETHERNET 0 25 | #define IS_LINUX 0 26 | #define NEED_UART_PATCH 0 27 | 28 | /* === LINUX DEFINITIONS AND MACROS === */ 29 | #elif defined(__linux__) 30 | #define USE_MUTEX 1 31 | #define HAS_FREERTOS 0 32 | #define HAS_ETHERNET 0 33 | #define IS_LINUX 1 34 | #define NEED_UART_PATCH 0 35 | #include // for printf() 36 | #include // for memcpy(), strlen() etc. 37 | #include // for uint32_t etc. 38 | #if IS_RASPBERRY 39 | #include 40 | #else 41 | #include // NOLINT 42 | // Use nanosleep() to avoid problems with pthreads (std::this_thread::sleep_for would interfere!) 43 | #define delay(x) nanosleep((const struct timespec[]){{x/1000, (x%1000)*1000000L}}, NULL); 44 | typedef std::chrono::steady_clock clk; 45 | #define millis() std::chrono::duration_cast(clk::now().time_since_epoch()).count() 46 | #define micros() std::chrono::duration_cast(clk::now().time_since_epoch()).count() 47 | #endif 48 | 49 | /* === INVALID TARGET === */ 50 | #else 51 | #error Define target in options.h 52 | #endif 53 | 54 | /* === COMMON MACROS === */ 55 | #if USE_MUTEX 56 | #define LOCK_GUARD(x,y) std::lock_guard x(y); 57 | #else 58 | #define LOCK_GUARD(x,y) 59 | #endif 60 | 61 | #endif // _EMODBUS_OPTIONS_H 62 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/scripts/README.md: -------------------------------------------------------------------------------- 1 | The scripts in this folder originate from [eModbus discussion 147](https://github.com/eModbus/eModbus/discussions/147). 2 | -------------------------------------------------------------------------------- /Software/src/lib/eModbus-eModbus/scripts/mbServerFCs.h: -------------------------------------------------------------------------------- 1 | #include "../ModbusServerRTU.h" 2 | 3 | #define MBTCP_ID 21 // modbus TCP server ID 4 | #define MBPV_MAX 30000 5 | 6 | ModbusMessage FC03(ModbusMessage request); 7 | ModbusMessage FC06(ModbusMessage request); 8 | ModbusMessage FC16(ModbusMessage request); 9 | ModbusMessage FC23(ModbusMessage request); 10 | -------------------------------------------------------------------------------- /Software/src/lib/mathieucarbou-AsyncTCPSock/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"AsyncTCPSock", 3 | "description":"Reimplementation of an Asynchronous TCP Library for ESP32, using BSD Sockets", 4 | "keywords":"async,tcp", 5 | "authors": 6 | { 7 | "name": "Alex Villacís Lasso", 8 | "maintainer": true 9 | }, 10 | "repository": 11 | { 12 | "type": "git", 13 | "url": "https://github.com/yubox-node-org/AsyncTCPSock.git" 14 | }, 15 | "version": "1.0.2-dev", 16 | "license": "LGPL-3.0", 17 | "frameworks": "arduino", 18 | "platforms": "espressif32", 19 | "build": { 20 | "libCompatMode": 2 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Software/src/lib/mathieucarbou-AsyncTCPSock/library.properties: -------------------------------------------------------------------------------- 1 | name=AsyncTCPSock 2 | version=1.0.2-dev 3 | author=avillacis 4 | maintainer=avillacis 5 | sentence=Reimplemented Async TCP Library for ESP32 using BSD Sockets 6 | paragraph=This is a reimplementation of AsyncTCP (Async TCP Library for ESP32) by Me No Dev, using high-level BSD Sockets instead of the low-level packet API and a message queue. 7 | category=Other 8 | url=https://github.com/yubox-node-org/AsyncTCPSock 9 | architectures=* 10 | -------------------------------------------------------------------------------- /Software/src/lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP_SSL.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNCTCP_SSL_H_ 2 | #define ASYNCTCP_SSL_H_ 3 | 4 | #include "AsyncTCP_SSL.hpp" 5 | 6 | #endif /* ASYNCTCP_SSL_H_ */ -------------------------------------------------------------------------------- /Software/src/lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP_SSL.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ASYNCTCP_SSL_HPP 2 | #define ASYNCTCP_SSL_HPP 3 | 4 | #ifdef ASYNC_TCP_SSL_ENABLED 5 | 6 | #include 7 | 8 | #define AsyncSSLClient AsyncClient 9 | #define AsyncSSLServer AsyncServer 10 | 11 | #define ASYNC_TCP_SSL_VERSION "AsyncTCPSock SSL shim v0.0.1" 12 | 13 | #else 14 | #error Compatibility shim requires ASYNC_TCP_SSL_ENABLED to be defined! 15 | #endif 16 | 17 | #endif -------------------------------------------------------------------------------- /Software/src/lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP_TLS_Context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if ASYNC_TCP_SSL_ENABLED 4 | 5 | #include "mbedtls/version.h" 6 | #include "mbedtls/platform.h" 7 | #if MBEDTLS_VERSION_NUMBER < 0x03000000 8 | #include "mbedtls/net.h" 9 | #else 10 | #include "mbedtls/net_sockets.h" 11 | #endif 12 | #include "mbedtls/debug.h" 13 | #include "mbedtls/ssl.h" 14 | #include "mbedtls/entropy.h" 15 | #include "mbedtls/ctr_drbg.h" 16 | #include "mbedtls/error.h" 17 | 18 | #define ASYNCTCP_TLS_CAN_RETRY(r) (((r) == MBEDTLS_ERR_SSL_WANT_READ) || ((r) == MBEDTLS_ERR_SSL_WANT_WRITE)) 19 | #define ASYNCTCP_TLS_EOF(r) (((r) == MBEDTLS_ERR_SSL_CONN_EOF) || ((r) == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)) 20 | 21 | class AsyncTCP_TLS_Context 22 | { 23 | private: 24 | // These fields must persist for the life of the encrypted connection, destroyed on 25 | // object destructor. 26 | mbedtls_ssl_context ssl_ctx; 27 | mbedtls_ssl_config ssl_conf; 28 | mbedtls_ctr_drbg_context drbg_ctx; 29 | mbedtls_entropy_context entropy_ctx; 30 | 31 | // These allocate memory during handshake but must be freed on either success or failure 32 | mbedtls_x509_crt ca_cert; 33 | mbedtls_x509_crt client_cert; 34 | mbedtls_pk_context client_key; 35 | bool _have_ca_cert; 36 | bool _have_client_cert; 37 | bool _have_client_key; 38 | 39 | unsigned long handshake_timeout; 40 | unsigned long handshake_start_time; 41 | 42 | int _socket; 43 | 44 | int _startSSLClient(int sck, const char * host_or_ip, 45 | const unsigned char *rootCABuff, const size_t rootCABuff_len, 46 | const unsigned char *cli_cert, const size_t cli_cert_len, 47 | const unsigned char *cli_key, const size_t cli_key_len, 48 | const char *pskIdent, const char *psKey, 49 | bool insecure); 50 | 51 | // Delete certificates used in handshake 52 | void _deleteHandshakeCerts(void); 53 | public: 54 | AsyncTCP_TLS_Context(void); 55 | virtual ~AsyncTCP_TLS_Context(); 56 | 57 | int startSSLClientInsecure(int sck, const char * host_or_ip); 58 | 59 | int startSSLClient(int sck, const char * host_or_ip, 60 | const char *pskIdent, const char *psKey); 61 | 62 | int startSSLClient(int sck, const char * host_or_ip, 63 | const char *rootCABuff, 64 | const char *cli_cert, 65 | const char *cli_key); 66 | 67 | int startSSLClient(int sck, const char * host_or_ip, 68 | const unsigned char *rootCABuff, const size_t rootCABuff_len, 69 | const unsigned char *cli_cert, const size_t cli_cert_len, 70 | const unsigned char *cli_key, const size_t cli_key_len); 71 | 72 | int runSSLHandshake(void); 73 | 74 | int write(const uint8_t *data, size_t len); 75 | 76 | int read(uint8_t * data, size_t len); 77 | }; 78 | 79 | #endif // ASYNC_TCP_SSL_ENABLED -------------------------------------------------------------------------------- /Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @section License 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2017, Thomas Barth, barth-dev.de 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, copy, 12 | * modify, merge, publish, distribute, sublicense, and/or sell copies 13 | * of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #ifndef __DRIVERS_CAN_CFG_H__ 30 | #define __DRIVERS_CAN_CFG_H__ 31 | 32 | #include "freertos/FreeRTOS.h" 33 | #include "freertos/queue.h" 34 | #include "freertos/task.h" 35 | #include "driver/gpio.h" 36 | #include "freertos/semphr.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | /** \brief CAN Node Bus speed */ 43 | typedef enum { 44 | CAN_SPEED_100KBPS = 100, /**< \brief CAN Node runs at 100kBit/s. */ 45 | CAN_SPEED_125KBPS = 125, /**< \brief CAN Node runs at 125kBit/s. */ 46 | CAN_SPEED_200KBPS = 200, /**< \brief CAN Node runs at 250kBit/s. */ 47 | CAN_SPEED_250KBPS = 250, /**< \brief CAN Node runs at 250kBit/s. */ 48 | CAN_SPEED_500KBPS = 500, /**< \brief CAN Node runs at 500kBit/s. */ 49 | CAN_SPEED_800KBPS = 800, /**< \brief CAN Node runs at 800kBit/s. */ 50 | CAN_SPEED_1000KBPS = 1000 /**< \brief CAN Node runs at 1000kBit/s. */ 51 | } CAN_speed_t; 52 | 53 | /** \brief CAN configuration structure */ 54 | typedef struct { 55 | CAN_speed_t speed; /**< \brief CAN speed. */ 56 | gpio_num_t tx_pin_id; /**< \brief TX pin. */ 57 | gpio_num_t rx_pin_id; /**< \brief RX pin. */ 58 | QueueHandle_t rx_queue; /**< \brief Handler to FreeRTOS RX queue. */ 59 | QueueHandle_t tx_queue; /**< \brief Handler to FreeRTOS TX queue. */ 60 | TaskHandle_t tx_handle; /**< \brief Handler to FreeRTOS TX task. */ 61 | TaskHandle_t rx_handle; /**< \brief Handler to FreeRTOS RX task. */ 62 | } CAN_device_t; 63 | 64 | /** \brief CAN configuration reference */ 65 | extern CAN_device_t CAN_cfg; 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif /* __DRIVERS_CAN_CFG_H__ */ 72 | -------------------------------------------------------------------------------- /Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp: -------------------------------------------------------------------------------- 1 | #include "ESP32CAN.h" 2 | #include 3 | #include "../../devboard/utils/events.h" 4 | 5 | int ESP32CAN::CANInit() { 6 | return CAN_init(); 7 | } 8 | bool ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame) { 9 | return CAN_write_frame(p_frame); 10 | } 11 | 12 | int ESP32CAN::CANStop() { 13 | return CAN_stop(); 14 | } 15 | int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter) { 16 | return CAN_config_filter(p_filter); 17 | } 18 | 19 | ESP32CAN ESP32Can; 20 | -------------------------------------------------------------------------------- /Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP32CAN_H 2 | #define ESP32CAN_H 3 | 4 | #include "../../lib/miwagner-ESP32-Arduino-CAN/CAN.h" 5 | #include "../../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h" 6 | 7 | class ESP32CAN { 8 | public: 9 | bool tx_ok = true; 10 | int CANInit(); 11 | int CANConfigFilter(const CAN_filter_t* p_filter); 12 | bool CANWriteFrame(const CAN_frame_t* p_frame); 13 | int CANStop(); 14 | void CANSetCfg(CAN_device_t* can_cfg); 15 | }; 16 | 17 | extern ESP32CAN ESP32Can; 18 | #endif 19 | -------------------------------------------------------------------------------- /Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // A CANFD driver 3 | // by Pierre Molinaro 4 | 5 | // This header is common to libraries 6 | // https://github.com/pierremolinaro/ACAN_T4 7 | // https://github.com/pierremolinaro/ACAN2517FD 8 | // 9 | //---------------------------------------------------------------------------------------------------------------------- 10 | 11 | #ifndef ACANFD_DATA_BIT_RATE_FACTOR_DEFINED 12 | #define ACANFD_DATA_BIT_RATE_FACTOR_DEFINED 13 | 14 | //---------------------------------------------------------------------------------------------------------------------- 15 | 16 | #include 17 | 18 | //---------------------------------------------------------------------------------------------------------------------- 19 | 20 | enum class DataBitRateFactor : uint8_t { 21 | x1 = 1, 22 | x2 = 2, 23 | x3 = 3, 24 | x4 = 4, 25 | x5 = 5, 26 | x6 = 6, 27 | x7 = 7, 28 | x8 = 8, 29 | x9 = 9, 30 | x10 = 10 31 | } ; 32 | 33 | //---------------------------------------------------------------------------------------------------------------------- 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // Generic CAN Message 3 | // by Pierre Molinaro 4 | // 5 | // This file is common to the following libraries 6 | // https://github.com/pierremolinaro/acan 7 | // https://github.com/pierremolinaro/acan2515 8 | // https://github.com/pierremolinaro/acan2517 9 | // https://github.com/pierremolinaro/acan2517FD 10 | // 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | 13 | #ifndef GENERIC_CAN_MESSAGE_DEFINED 14 | #define GENERIC_CAN_MESSAGE_DEFINED 15 | 16 | //---------------------------------------------------------------------------------------------------------------------- 17 | 18 | #include 19 | 20 | //---------------------------------------------------------------------------------------------------------------------- 21 | 22 | class CANMessage { 23 | public : uint32_t id = 0 ; // Frame identifier 24 | public : bool ext = false ; // false -> standard frame, true -> extended frame 25 | public : bool rtr = false ; // false -> data frame, true -> remote frame 26 | public : uint8_t idx = 0 ; // This field is used by the driver 27 | public : uint8_t len = 0 ; // Length of data (0 ... 8) 28 | public : union { 29 | uint64_t data64 ; // Caution: subject to endianness 30 | int64_t data_s64 ; // Caution: subject to endianness 31 | uint32_t data32 [2] ; // Caution: subject to endianness 32 | int32_t data_s32 [2] ; // Caution: subject to endianness 33 | float dataFloat [2] ; // Caution: subject to endianness 34 | uint16_t data16 [4] ; // Caution: subject to endianness 35 | int16_t data_s16 [4] ; // Caution: subject to endianness 36 | int8_t data_s8 [8] ; 37 | uint8_t data [8] = {0, 0, 0, 0, 0, 0, 0, 0} ; 38 | } ; 39 | } ; 40 | 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | 43 | typedef enum {kStandard, kExtended} tFrameFormat ; 44 | typedef enum {kData, kRemote} tFrameKind ; 45 | typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ; 46 | 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Software/src/lib/pierremolinaro-ACAN2517FD/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Pierre Molinaro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Software/src/lib/pierremolinaro-acan2515/CANMessage.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // Generic CAN Message 3 | // by Pierre Molinaro 4 | // 5 | // This file is common to the following libraries 6 | // https://github.com/pierremolinaro/acan 7 | // https://github.com/pierremolinaro/acan2515 8 | // https://github.com/pierremolinaro/acan2517 9 | // https://github.com/pierremolinaro/acan2517FD 10 | // 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | 13 | #ifndef GENERIC_CAN_MESSAGE_DEFINED 14 | #define GENERIC_CAN_MESSAGE_DEFINED 15 | 16 | //---------------------------------------------------------------------------------------------------------------------- 17 | 18 | #include 19 | 20 | //---------------------------------------------------------------------------------------------------------------------- 21 | 22 | class CANMessage { 23 | public : uint32_t id = 0 ; // Frame identifier 24 | public : bool ext = false ; // false -> standard frame, true -> extended frame 25 | public : bool rtr = false ; // false -> data frame, true -> remote frame 26 | public : uint8_t idx = 0 ; // This field is used by the driver 27 | public : uint8_t len = 0 ; // Length of data (0 ... 8) 28 | public : union { 29 | uint64_t data64 ; // Caution: subject to endianness 30 | int64_t data_s64 ; // Caution: subject to endianness 31 | uint32_t data32 [2] ; // Caution: subject to endianness 32 | int32_t data_s32 [2] ; // Caution: subject to endianness 33 | float dataFloat [2] ; // Caution: subject to endianness 34 | uint16_t data16 [4] ; // Caution: subject to endianness 35 | int16_t data_s16 [4] ; // Caution: subject to endianness 36 | int8_t data_s8 [8] ; 37 | uint8_t data [8] = {0, 0, 0, 0, 0, 0, 0, 0} ; 38 | } ; 39 | } ; 40 | 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | 43 | typedef enum {kStandard, kExtended} tFrameFormat ; 44 | typedef enum {kData, kRemote} tFrameKind ; 45 | typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ; 46 | 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Software/src/lib/pierremolinaro-acan2515/MCP2515ReceiveFilters.h: -------------------------------------------------------------------------------- 1 | //·································································································· 2 | // MCP2515 Receive filter classes 3 | // by Pierre Molinaro 4 | // https://github.com/pierremolinaro/acan2515 5 | //·································································································· 6 | 7 | #ifndef MCP2515_RECEIVE_FILTER_ENTITIES_DEFINED 8 | #define MCP2515_RECEIVE_FILTER_ENTITIES_DEFINED 9 | 10 | //·································································································· 11 | 12 | #include 13 | 14 | //·································································································· 15 | 16 | class ACAN2515Mask { 17 | 18 | //--- Default constructor 19 | public: ACAN2515Mask (void) : 20 | mSIDH (0), 21 | mSIDL (0), 22 | mEID8 (0), 23 | mEID0 (0) { 24 | } 25 | 26 | //--- Properties 27 | public: uint8_t mSIDH ; 28 | public: uint8_t mSIDL ; 29 | public: uint8_t mEID8 ; 30 | public: uint8_t mEID0 ; 31 | } ; 32 | 33 | //·································································································· 34 | 35 | class ACAN2515AcceptanceFilter { 36 | public: typedef void (*tCallBackRoutine) (const CANMessage & inMessage) ; 37 | public: const ACAN2515Mask mMask ; 38 | public: const tCallBackRoutine mCallBack ; 39 | } ; 40 | 41 | //·································································································· 42 | 43 | inline ACAN2515Mask standard2515Mask (const uint16_t inIdentifier, 44 | const uint8_t inByte0, 45 | const uint8_t inByte1) { 46 | ACAN2515Mask result ; 47 | result.mSIDH = (uint8_t) (inIdentifier >> 3) ; 48 | result.mSIDL = (uint8_t) (inIdentifier << 5) ; 49 | result.mEID8 = inByte0 ; 50 | result.mEID0 = inByte1 ; 51 | return result ; 52 | } 53 | 54 | //·································································································· 55 | 56 | inline ACAN2515Mask extended2515Mask (const uint32_t inIdentifier) { 57 | ACAN2515Mask result ; 58 | result.mSIDH = (uint8_t) (inIdentifier >> 21) ; 59 | result.mSIDL = (uint8_t) (((inIdentifier >> 16) & 0x03) | ((inIdentifier >> 13) & 0xE0)) ; 60 | result.mEID8 = (uint8_t) (inIdentifier >> 8) ; 61 | result.mEID0 = (uint8_t) inIdentifier ; 62 | return result ; 63 | } 64 | 65 | //·································································································· 66 | 67 | inline ACAN2515Mask standard2515Filter (const uint16_t inIdentifier, 68 | const uint8_t inByte0, 69 | const uint8_t inByte1) { 70 | ACAN2515Mask result ; 71 | result.mSIDH = (uint8_t) (inIdentifier >> 3) ; 72 | result.mSIDL = (uint8_t) (inIdentifier << 5) ; 73 | result.mEID8 = inByte0 ; 74 | result.mEID0 = inByte1 ; 75 | return result ; 76 | } 77 | 78 | //·································································································· 79 | 80 | inline ACAN2515Mask extended2515Filter (const uint32_t inIdentifier) { 81 | ACAN2515Mask result ; 82 | result.mSIDH = (uint8_t) (inIdentifier >> 21) ; 83 | result.mSIDL = (uint8_t) (((inIdentifier >> 16) & 0x03) | ((inIdentifier >> 13) & 0xE0)) | 0x08 ; 84 | result.mEID8 = (uint8_t) (inIdentifier >> 8) ; 85 | result.mEID0 = (uint8_t) inIdentifier ; 86 | return result ; 87 | } 88 | 89 | //·································································································· 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /Software/src/system_settings.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSTEM_SETTINGS_H_ 2 | #define SYSTEM_SETTINGS_H_ 3 | /** TASKS 4 | * Higher number equals higher priority. Max 25 per core 5 | * 6 | * Parameter: TASK_CORE_PRIO 7 | * Description: 8 | * Defines the priority of core functionality (CAN, Modbus, etc) 9 | * 10 | * Parameter: TASK_CONNECTIVITY_PRIO 11 | * Description: 12 | * Defines the priority of various wireless functionality (TCP, MQTT, etc) 13 | * 14 | * Parameter: TASK_MODBUS_PRIO 15 | * Description: 16 | * Defines the priority of MODBUS handling 17 | * 18 | * Parameter: TASK_ACAN2515_PRIORITY 19 | * Description: 20 | * Defines the priority of ACAN2515 CAN handling 21 | * 22 | * Parameter: TASK_ACAN2515_PRIORITY 23 | * Description: 24 | * Defines the priority of ACAN2517FD CAN-FD handling 25 | */ 26 | #define TASK_CORE_PRIO 4 27 | #define TASK_CONNECTIVITY_PRIO 3 28 | #define TASK_MQTT_PRIO 2 29 | #define TASK_MODBUS_PRIO 8 30 | #define TASK_ACAN2515_PRIORITY 10 31 | #define TASK_ACAN2517FD_PRIORITY 10 32 | 33 | /** MAX AMOUNT OF CELLS 34 | * 35 | * Parameter: MAX_AMOUNT_CELLS 36 | * Description: 37 | * Basically the length of the array used to hold individual cell voltages 38 | */ 39 | #define MAX_AMOUNT_CELLS 192 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /cmake_clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Cleaning up 3 | rmdir /Q/S build 4 | echo Creating new CMake build folder 5 | mkdir build 6 | cd build 7 | echo Building CMake project 8 | call cmake .. 9 | call cmake --build . 10 | echo Executing tests 11 | for %%i in ("test\Debug\*.exe") do ( 12 | echo Running %%i 13 | %%i 14 | ) 15 | cd.. 16 | -------------------------------------------------------------------------------- /min_spiffs.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, 0xe000, 0x2000, 4 | app0, app, ota_0, 0x10000, 0x1E0000, 5 | app1, app, ota_1, 0x1F0000,0x1E0000, 6 | spiffs, data, spiffs, 0x3D0000,0x20000, 7 | coredump, data, coredump,0x3F0000,0x10000, -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | src_dir = ./Software 13 | 14 | [env:esp32dev] 15 | platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip 16 | board = esp32dev 17 | monitor_speed = 115200 18 | monitor_filters = default, time, log2file 19 | board_build.partitions = min_spiffs.csv 20 | framework = arduino 21 | build_flags = -I include 22 | lib_deps = 23 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Include the directory with your source files 2 | include_directories(${CMAKE_SOURCE_DIR}/Software/src/devboard ${CMAKE_SOURCE_DIR}/Software/src/devboard/utils . ) 3 | 4 | # Create a variable to store the list of test files 5 | file(GLOB TEST_SOURCES utils/*.cpp) 6 | 7 | # Loop through each test source file and create an executable 8 | foreach(TEST_SOURCE ${TEST_SOURCES}) 9 | # Extract the test name without extension 10 | get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) 11 | 12 | # Create an executable for the test 13 | add_executable(${TEST_NAME} ${TEST_SOURCE} test_lib.cpp) 14 | 15 | # Apply the target_compile_definitions for the test 16 | target_compile_definitions(${TEST_NAME} PRIVATE UNIT_TEST) 17 | 18 | endforeach() 19 | -------------------------------------------------------------------------------- /test/test_lib.cpp: -------------------------------------------------------------------------------- 1 | #include "test_lib.h" 2 | 3 | #include 4 | 5 | MySerial Serial; 6 | 7 | unsigned long testlib_millis = 0; 8 | 9 | uint8_t bms_status = ACTIVE; 10 | 11 | uint8_t LEDcolor = GREEN; 12 | -------------------------------------------------------------------------------- /test/test_lib.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_LIB_H__ 2 | #define __TEST_LIB_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "microtest.h" 10 | 11 | using namespace std; 12 | 13 | class MySerial; 14 | 15 | extern unsigned long testlib_millis; 16 | extern MySerial Serial; 17 | extern uint8_t bms_status; 18 | extern uint8_t LEDcolor; 19 | 20 | /* Mock millis() */ 21 | static inline unsigned long millis(void) { 22 | return testlib_millis; 23 | } 24 | 25 | /* Mock Serial class */ 26 | class MySerial { 27 | public: 28 | size_t println(const char* s) { 29 | return print(s, true); // Call print with newline argument true 30 | } 31 | 32 | size_t print(const char* s) { 33 | return print(s, false); // Call print with newline argument false 34 | } 35 | 36 | private: 37 | size_t print(const char* s, bool newline) { 38 | size_t length = printf("%s", s); // Print the string without newline 39 | if (newline) { 40 | printf("\n"); // Add a newline if specified 41 | length++; // Increment length to account for the added newline character 42 | } 43 | return length; // Return the total length printed 44 | } 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /test/utils/events_test.cpp_: -------------------------------------------------------------------------------- 1 | // The test library must be included first! 2 | #include "../test_lib.h" 3 | 4 | #include "../../Software/src/include.h" 5 | #include "../../Software/src/devboard/utils/timer.cpp" 6 | 7 | 8 | class EEPROMClass { 9 | public: 10 | void begin(int s) {} 11 | void writeUShort(int a, uint16_t d) {} 12 | void commit(void) {} 13 | uint16_t readUShort(int a) {} 14 | 15 | template 16 | void get(int address, T &t) {} 17 | 18 | template 19 | void put(int address, const T &t) {} 20 | }; 21 | 22 | EEPROMClass EEPROM; 23 | 24 | #include "../../Software/src/devboard/utils/events.cpp" 25 | /* Local test variables */ 26 | bool elapsed = false; 27 | 28 | /* Stubs */ 29 | void run_sequence_on_target(void) {} 30 | 31 | /* Helper functions */ 32 | /* Test functions */ 33 | 34 | TEST(init_events_test) { 35 | init_events(); 36 | 37 | for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) { 38 | ASSERT_EQ(events.entries[i].occurences, 0); 39 | ASSERT_EQ(events.entries[i].data, 0); 40 | ASSERT_EQ(events.entries[i].timestamp, 0); 41 | } 42 | } 43 | 44 | TEST(update_event_time_test) { 45 | // Reset 46 | testlib_millis = 0; 47 | events.time_seconds = 0; 48 | init_events(); 49 | 50 | // No delta, so time shouldn't increase 51 | update_event_time(); 52 | ASSERT_EQ(events.time_seconds, 0); 53 | 54 | // Almost time to bump the seconds 55 | testlib_millis = 999; 56 | update_event_time(); 57 | ASSERT_EQ(events.time_seconds, 0); 58 | 59 | // millis == 1000, so we should add a second 60 | testlib_millis = 1000; 61 | update_event_time(); 62 | ASSERT_EQ(events.time_seconds, 1); 63 | 64 | // We shouldn't add more seconds until 2000 now 65 | testlib_millis = 1999; 66 | update_event_time(); 67 | ASSERT_EQ(events.time_seconds, 1); 68 | testlib_millis = 2000; 69 | update_event_time(); 70 | ASSERT_EQ(events.time_seconds, 2); 71 | } 72 | 73 | TEST(set_event_test) { 74 | // Reset 75 | init_events(); 76 | events.time_seconds = 0; 77 | 78 | // Initially, the event should not have any data or occurences 79 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 0); 80 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 0); 81 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 0); 82 | // Set current time and overvoltage event for cell 23 (RED color, bms_status == FAULT) 83 | events.time_seconds = 345; 84 | set_event(EVENT_CELL_OVER_VOLTAGE, 123); 85 | // Ensure proper event data 86 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 123); 87 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 1); 88 | ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 345); 89 | ASSERT_EQ(bms_status, FAULT); 90 | } 91 | 92 | TEST(events_message_test) { 93 | set_event(EVENT_DUMMY_ERROR, 0); // Set dummy event with no data 94 | 95 | ASSERT_STREQ("The dummy error event was set!", get_event_message_string(EVENT_DUMMY_ERROR)); 96 | } 97 | 98 | TEST(events_level_test) { 99 | init_events(); 100 | set_event(EVENT_DUMMY_ERROR, 0); // Set dummy event with no data 101 | 102 | ASSERT_STREQ("ERROR", get_event_level_string(EVENT_DUMMY_ERROR)); 103 | } 104 | 105 | TEST_MAIN(); 106 | --------------------------------------------------------------------------------