├── .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