├── .clang-tidy
├── .editorconfig
├── .gitignore
├── CMakeLists.txt
├── FUNDING.yml
├── LICENSE.md
├── README.md
├── arduino.sh
├── docs
├── metadata.md
├── security.md
├── session.md
└── shared.md
├── examples
├── arduino
│ └── arduino.ino
├── esp32
│ ├── .gitignore
│ ├── .vscode
│ │ └── extensions.json
│ ├── CMakeLists.txt
│ ├── platformio.ini
│ ├── sdkconfig.seeed_xiao_esp32c3
│ └── src
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
└── simple
│ ├── CMakeLists.txt
│ ├── ble.cpp
│ ├── include
│ └── ble.h
│ └── src
│ └── ble.cpp
├── include
├── authenticator.h
├── car_server.pb.h
├── carserver.h
├── common.pb.h
├── errors.pb.h
├── keys.pb.h
├── metadata.h
├── security.h
├── session.h
├── shared.h
├── signatures.pb.h
├── universal_message.pb.h
├── vcsec.pb.h
└── vehicle.pb.h
├── library.json
├── patches
└── mbedtls2.patch
├── proto.sh
├── protobuf
├── car_server.options
├── car_server.proto
├── common.proto
├── errors.proto
├── keys.proto
├── signatures.options
├── signatures.proto
├── universal_message.options
├── universal_message.proto
├── vcsec.options
├── vcsec.proto
└── vehicle.proto
└── src
├── authenticator.cpp
├── car_server.pb.c
├── carserver.cpp
├── common.pb.c
├── errors.pb.c
├── keys.pb.c
├── metadata.cpp
├── security.cpp
├── session.cpp
├── shared.cpp
├── signatures.pb.c
├── universal_message.pb.c
├── vcsec.pb.c
└── vehicle.pb.c
/.clang-tidy:
--------------------------------------------------------------------------------
1 | # Generated from CLion Inspection settings
2 | ---
3 | Checks: '-*,
4 | bugprone-argument-comment,
5 | bugprone-assert-side-effect,
6 | bugprone-bad-signal-to-kill-thread,
7 | bugprone-branch-clone,
8 | bugprone-copy-constructor-init,
9 | bugprone-dangling-handle,
10 | bugprone-dynamic-static-initializers,
11 | bugprone-fold-init-type,
12 | bugprone-forward-declaration-namespace,
13 | bugprone-forwarding-reference-overload,
14 | bugprone-inaccurate-erase,
15 | bugprone-incorrect-roundings,
16 | bugprone-integer-division,
17 | bugprone-lambda-function-name,
18 | bugprone-macro-parentheses,
19 | bugprone-macro-repeated-side-effects,
20 | bugprone-misplaced-operator-in-strlen-in-alloc,
21 | bugprone-misplaced-pointer-arithmetic-in-alloc,
22 | bugprone-misplaced-widening-cast,
23 | bugprone-move-forwarding-reference,
24 | bugprone-multiple-statement-macro,
25 | bugprone-no-escape,
26 | bugprone-parent-virtual-call,
27 | bugprone-posix-return,
28 | bugprone-reserved-identifier,
29 | bugprone-sizeof-container,
30 | bugprone-sizeof-expression,
31 | bugprone-spuriously-wake-up-functions,
32 | bugprone-string-constructor,
33 | bugprone-string-integer-assignment,
34 | bugprone-string-literal-with-embedded-nul,
35 | bugprone-suspicious-enum-usage,
36 | bugprone-suspicious-include,
37 | bugprone-suspicious-memset-usage,
38 | bugprone-suspicious-missing-comma,
39 | bugprone-suspicious-semicolon,
40 | bugprone-suspicious-string-compare,
41 | bugprone-suspicious-memory-comparison,
42 | bugprone-suspicious-realloc-usage,
43 | bugprone-swapped-arguments,
44 | bugprone-terminating-continue,
45 | bugprone-throw-keyword-missing,
46 | bugprone-too-small-loop-variable,
47 | bugprone-undefined-memory-manipulation,
48 | bugprone-undelegated-constructor,
49 | bugprone-unhandled-self-assignment,
50 | bugprone-unused-raii,
51 | bugprone-unused-return-value,
52 | bugprone-use-after-move,
53 | bugprone-virtual-near-miss,
54 | cert-dcl21-cpp,
55 | cert-dcl58-cpp,
56 | cert-err34-c,
57 | cert-err52-cpp,
58 | cert-err60-cpp,
59 | cert-flp30-c,
60 | cert-msc50-cpp,
61 | cert-msc51-cpp,
62 | cert-str34-c,
63 | cppcoreguidelines-interfaces-global-init,
64 | cppcoreguidelines-narrowing-conversions,
65 | cppcoreguidelines-pro-type-member-init,
66 | cppcoreguidelines-pro-type-static-cast-downcast,
67 | cppcoreguidelines-slicing,
68 | google-default-arguments,
69 | google-explicit-constructor,
70 | google-runtime-operator,
71 | hicpp-exception-baseclass,
72 | hicpp-multiway-paths-covered,
73 | misc-misplaced-const,
74 | misc-new-delete-overloads,
75 | misc-no-recursion,
76 | misc-non-copyable-objects,
77 | misc-throw-by-value-catch-by-reference,
78 | misc-unconventional-assign-operator,
79 | misc-uniqueptr-reset-release,
80 | modernize-avoid-bind,
81 | modernize-concat-nested-namespaces,
82 | modernize-deprecated-headers,
83 | modernize-deprecated-ios-base-aliases,
84 | modernize-loop-convert,
85 | modernize-make-shared,
86 | modernize-make-unique,
87 | modernize-pass-by-value,
88 | modernize-raw-string-literal,
89 | modernize-redundant-void-arg,
90 | modernize-replace-auto-ptr,
91 | modernize-replace-disallow-copy-and-assign-macro,
92 | modernize-replace-random-shuffle,
93 | modernize-return-braced-init-list,
94 | modernize-shrink-to-fit,
95 | modernize-unary-static-assert,
96 | modernize-use-auto,
97 | modernize-use-bool-literals,
98 | modernize-use-emplace,
99 | modernize-use-equals-default,
100 | modernize-use-equals-delete,
101 | modernize-use-nodiscard,
102 | modernize-use-noexcept,
103 | modernize-use-nullptr,
104 | modernize-use-override,
105 | modernize-use-transparent-functors,
106 | modernize-use-uncaught-exceptions,
107 | mpi-buffer-deref,
108 | mpi-type-mismatch,
109 | openmp-use-default-none,
110 | performance-faster-string-find,
111 | performance-for-range-copy,
112 | performance-implicit-conversion-in-loop,
113 | performance-inefficient-algorithm,
114 | performance-inefficient-string-concatenation,
115 | performance-inefficient-vector-operation,
116 | performance-move-const-arg,
117 | performance-move-constructor-init,
118 | performance-no-automatic-move,
119 | performance-noexcept-move-constructor,
120 | performance-trivially-destructible,
121 | performance-type-promotion-in-math-fn,
122 | performance-unnecessary-copy-initialization,
123 | performance-unnecessary-value-param,
124 | portability-simd-intrinsics,
125 | readability-avoid-const-params-in-decls,
126 | readability-const-return-type,
127 | readability-container-size-empty,
128 | readability-convert-member-functions-to-static,
129 | readability-delete-null-pointer,
130 | readability-deleted-default,
131 | readability-inconsistent-declaration-parameter-name,
132 | readability-make-member-function-const,
133 | readability-misleading-indentation,
134 | readability-misplaced-array-index,
135 | readability-non-const-parameter,
136 | readability-redundant-control-flow,
137 | readability-redundant-declaration,
138 | readability-redundant-function-ptr-dereference,
139 | readability-redundant-smartptr-get,
140 | readability-redundant-string-cstr,
141 | readability-redundant-string-init,
142 | readability-simplify-subscript-expr,
143 | readability-static-accessed-through-instance,
144 | readability-static-definition-in-anonymous-namespace,
145 | readability-string-compare,
146 | readability-uniqueptr-delete-release,
147 | readability-use-anyofallof'
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | max_line_length = 150
11 |
12 | [*.md]
13 | max_line_length = off
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/intellij,c++,cmake,platformio,visualstudiocode
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=intellij,c++,cmake,platformio,visualstudiocode
3 |
4 | ### C++ ###
5 | # Prerequisites
6 | *.d
7 |
8 | # Compiled Object files
9 | *.slo
10 | *.lo
11 | *.o
12 | *.obj
13 |
14 | # Precompiled Headers
15 | *.gch
16 | *.pch
17 |
18 | # Compiled Dynamic libraries
19 | *.so
20 | *.dylib
21 | *.dll
22 |
23 | # Fortran module files
24 | *.mod
25 | *.smod
26 |
27 | # Compiled Static libraries
28 | *.lai
29 | *.la
30 | *.a
31 | *.lib
32 |
33 | # Executables
34 | *.exe
35 | *.out
36 | *.app
37 |
38 | ### CMake ###
39 | CMakeLists.txt.user
40 | CMakeCache.txt
41 | CMakeFiles
42 | CMakeScripts
43 | Testing
44 | Makefile
45 | cmake_install.cmake
46 | install_manifest.txt
47 | compile_commands.json
48 | CTestTestfile.cmake
49 | _deps
50 |
51 | ### CMake Patch ###
52 | # External projects
53 | *-prefix/
54 |
55 | ### Intellij ###
56 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
57 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
58 |
59 | # User-specific stuff
60 | .idea/**/workspace.xml
61 | .idea/**/tasks.xml
62 | .idea/**/usage.statistics.xml
63 | .idea/**/dictionaries
64 | .idea/**/shelf
65 |
66 | # AWS User-specific
67 | .idea/**/aws.xml
68 |
69 | # Generated files
70 | .idea/**/contentModel.xml
71 |
72 | # Sensitive or high-churn files
73 | .idea/**/dataSources/
74 | .idea/**/dataSources.ids
75 | .idea/**/dataSources.local.xml
76 | .idea/**/sqlDataSources.xml
77 | .idea/**/dynamic.xml
78 | .idea/**/uiDesigner.xml
79 | .idea/**/dbnavigator.xml
80 |
81 | # Gradle
82 | .idea/**/gradle.xml
83 | .idea/**/libraries
84 |
85 | # Gradle and Maven with auto-import
86 | # When using Gradle or Maven with auto-import, you should exclude module files,
87 | # since they will be recreated, and may cause churn. Uncomment if using
88 | # auto-import.
89 | # .idea/artifacts
90 | # .idea/compiler.xml
91 | # .idea/jarRepositories.xml
92 | # .idea/modules.xml
93 | # .idea/*.iml
94 | # .idea/modules
95 | # *.iml
96 | # *.ipr
97 |
98 | # CMake
99 | cmake-build-*/
100 |
101 | # Mongo Explorer plugin
102 | .idea/**/mongoSettings.xml
103 |
104 | # File-based project format
105 | *.iws
106 |
107 | # IntelliJ
108 | out/
109 |
110 | # mpeltonen/sbt-idea plugin
111 | .idea_modules/
112 |
113 | # JIRA plugin
114 | atlassian-ide-plugin.xml
115 |
116 | # Cursive Clojure plugin
117 | .idea/replstate.xml
118 |
119 | # SonarLint plugin
120 | .idea/sonarlint/
121 |
122 | # Crashlytics plugin (for Android Studio and IntelliJ)
123 | com_crashlytics_export_strings.xml
124 | crashlytics.properties
125 | crashlytics-build.properties
126 | fabric.properties
127 |
128 | # Editor-based Rest Client
129 | .idea/httpRequests
130 |
131 | # Android studio 3.1+ serialized cache file
132 | .idea/caches/build_file_checksums.ser
133 |
134 | ### Intellij Patch ###
135 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
136 |
137 | # *.iml
138 | # modules.xml
139 | # .idea/misc.xml
140 | # *.ipr
141 |
142 | # Sonarlint plugin
143 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
144 | .idea/**/sonarlint/
145 |
146 | # SonarQube Plugin
147 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
148 | .idea/**/sonarIssues.xml
149 |
150 | # Markdown Navigator plugin
151 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
152 | .idea/**/markdown-navigator.xml
153 | .idea/**/markdown-navigator-enh.xml
154 | .idea/**/markdown-navigator/
155 |
156 | # Cache file creation bug
157 | # See https://youtrack.jetbrains.com/issue/JBR-2257
158 | .idea/$CACHE_FILE$
159 |
160 | # CodeStream plugin
161 | # https://plugins.jetbrains.com/plugin/12206-codestream
162 | .idea/codestream.xml
163 |
164 | # Azure Toolkit for IntelliJ plugin
165 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
166 | .idea/**/azureSettings.xml
167 | .idea/
168 |
169 | ### PlatformIO ###
170 | .pioenvs
171 | .piolibdeps
172 | .clang_complete
173 | .gcc-flags.json
174 | .pio
175 |
176 | ### VisualStudioCode ###
177 | .vscode/*
178 | !.vscode/settings.json
179 | !.vscode/tasks.json
180 | !.vscode/launch.json
181 | !.vscode/extensions.json
182 | !.vscode/*.code-snippets
183 |
184 | # Local History for Visual Studio Code
185 | .history/
186 |
187 | # Built Visual Studio Code Extensions
188 | *.vsix
189 |
190 | ### VisualStudioCode Patch ###
191 | # Ignore all local history of files
192 | .history
193 | .ionide
194 |
195 | # syncthing
196 | .stfolder
197 |
198 | build/
199 | library.zip
200 |
201 | # End of https://www.toptal.com/developers/gitignore/api/intellij,c++,cmake,platformio,visualstudiocode
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.20)
2 | include(FetchContent)
3 | project(TeslaBLE
4 | VERSION 1.0.0
5 | DESCRIPTION "CPP Tesla BLE Library"
6 | LANGUAGES CXX C
7 | )
8 |
9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0")
10 | set(MBEDTLS_AS_SUBPROJECT ON)
11 | set(ENABLE_PROGRAMS OFF)
12 | set(ENABLE_TESTING OFF)
13 | set(BUILD_EXAMPLES ON)
14 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
15 |
16 | FetchContent_Declare(
17 | nanopb
18 | GIT_REPOSITORY https://github.com/nanopb/nanopb.git
19 | GIT_TAG master
20 | GIT_SHALLOW TRUE
21 | )
22 | FetchContent_Declare(
23 | mbedtls
24 | GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls.git
25 | GIT_TAG v3.6.0
26 | GIT_SHALLOW TRUE
27 | )
28 |
29 | FetchContent_MakeAvailable(nanopb mbedtls)
30 |
31 | # Common source files
32 | set(COMMON_SOURCES
33 | src/authenticator.cpp
34 | src/metadata.cpp
35 | src/session.cpp
36 | src/shared.cpp
37 | src/carserver.cpp
38 | src/security.cpp
39 | )
40 |
41 | # NanoPB source files
42 | set(NANOPB_SOURCES
43 | ${nanopb_SOURCE_DIR}/pb_decode.c
44 | ${nanopb_SOURCE_DIR}/pb_encode.c
45 | ${nanopb_SOURCE_DIR}/pb_common.c
46 | )
47 |
48 | # Protobuf source files
49 | set(PROTOBUF_SOURCES
50 | src/car_server.pb.c
51 | src/common.pb.c
52 | src/errors.pb.c
53 | src/keys.pb.c
54 | src/signatures.pb.c
55 | src/universal_message.pb.c
56 | src/vcsec.pb.c
57 | src/vehicle.pb.c
58 | )
59 |
60 | set(ALL_SOURCES ${COMMON_SOURCES} ${NANOPB_SOURCES} ${PROTOBUF_SOURCES})
61 | add_library(TeslaBLE STATIC ${ALL_SOURCES})
62 |
63 | # Include directories
64 | target_include_directories(TeslaBLE
65 | PRIVATE
66 | src/
67 | include/
68 | PUBLIC
69 | include/
70 | include/protobuf/
71 | ${nanopb_SOURCE_DIR}
72 | ${NANOPB_INCLUDE_DIRS}
73 | ${CMAKE_CURRENT_BINARY_DIR}
74 | )
75 |
76 | # Link libraries
77 | target_link_libraries(TeslaBLE PUBLIC mbedcrypto mbedtls mbedx509)
78 |
79 | # Public headers
80 | set(PUBLIC_HEADERS
81 | include/authenticator.h
82 | include/metadata.h
83 | include/shared.h
84 | include/carserver.h
85 | include/security.h
86 | include/car_server.pb.h
87 | include/common.pb.h
88 | include/errors.pb.h
89 | include/keys.pb.h
90 | include/signatures.pb.h
91 | include/universal_message.pb.h
92 | include/vcsec.pb.h
93 | include/vehicle.pb.h
94 | )
95 |
96 | # Set public headers
97 | foreach (HEADER ${PUBLIC_HEADERS})
98 | set_target_properties(TeslaBLE PROPERTIES PUBLIC_HEADER ${HEADER})
99 | endforeach ()
100 |
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: pmdroid
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Tesla Vehicle Command (CPP/Arduino)
2 | Copyright (C) 2024 Pascal Matthiesen
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Affero General Public License as published
6 | by the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU Affero General Public License for more details.
13 |
14 | You should have received a copy of the GNU Affero General Public License
15 | along with this program. If not, see .
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Tesla Vehicle Command (CPP/Arduino) / TeslaBLE
2 |
3 | This library is the successor of the [TeslaBLE](https://github.com/pmdroid/tesla-ble) library. Tesla vehicles now
4 | support a new protocol that ensures secure,
5 | end-to-end command authentication.
6 | This C++ and Arduino library uses this protocol to control vehicle functions, such as climate control and charging.
7 |
8 | It is available under the terms of the GNU Affero General Public License (AGPL), with an option for businesses to
9 | purchase a commercial license.
10 |
11 | ### Initialization
12 |
13 | To begin using the TeslaBLE library, create instances of `Authenticator` and `Session`.
14 |
15 | ```c++
16 | TeslaBLE::Authenticator authenticator;
17 | TeslaBLE::Session session;
18 | ```
19 |
20 | ### Private Key
21 |
22 | A private key is essential for communication with the Tesla vehicle. You can either load an existing key or generate a
23 | new one. This key must be securely stored for future use.
24 |
25 | ```c++
26 | unsigned char private_key[227] =
27 | "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----";
28 |
29 | client.LoadPrivateKey(private_key, sizeof private_key);
30 | ```
31 |
32 | If the private key hasn't been whitelisted with the car, generate a whitelist message and send it. Be aware that the car
33 | may require NFC card confirmation or display a UI prompt.
34 |
35 | ```c++
36 | authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, sessionInfoRequestBuffer,
37 | &sessionInfoRequestBufferLength);
38 | ```
39 |
40 | **Upon message transmission, the vehicle will return an OperationStatus_E enumeration:**
41 |
42 | * **OPERATIONSTATUS_OK:** Operation successful.
43 | * **OPERATIONSTATUS_WAIT:** Vehicle awaiting NFC card presentation.
44 | * **OPERATIONSTATUS_ERROR:** Operation failed.
45 |
46 | **A `OPERATIONSTATUS_WAIT` response indicates that the NFC card has not yet been presented to the vehicle.**
47 |
48 | ### Session Setup
49 |
50 | To utilize the `Session`, provide the Vehicle Identification Number (VIN), routing address, and loaded `Authenticator`.
51 | The Session class will track essential data such as counter, epoch, and the vehicle's current time.
52 |
53 | ```c++
54 | unsigned char vin[18]; // XP7YGCEL9NB000000
55 | strcpy((char *) vin, "XP7YGCEL9NB000000");
56 |
57 | session.SetVIN(vin);
58 | session.GenerateRoutingAddress(); // Or session.SetRoutingAddress()
59 | session.LoadAuthenticator(&authenticator);
60 | ```
61 |
62 | To conserve memory, a single Authenticator instance is shared among all system domains. This instance stores the
63 | exchanged secret essential for message encryption.
64 |
65 | ### Obtaining Session Information
66 |
67 | **Prior to transmitting any messages to the vehicle, it is imperative to obtain the latest session information by
68 | constructing and dispatching a `RequestSessionInfo` message.** The acquired session data should be retained for
69 | subsequent interactions. It is essential to note that the vehicle may provide updated session details, particularly
70 | following software revisions.
71 |
72 | ```c++
73 | unsigned char securitySessionInfoRequestBuffer[200];
74 | size_t securitySessionInfoRequestBufferLength = 0;
75 |
76 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY,
77 | securitySessionInfoRequestBuffer,
78 | &sessionInfoRequestBufferLength);
79 |
80 | session.UpdateSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_info, 92);
81 | ```
82 |
83 | This command exports the session data to the specified session_buffer and populates the session_size variable with the
84 | exported data's length.
85 |
86 | ```c++
87 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_buffer,
88 | &session_size);
89 | ```
90 |
91 | ### Sending Commands
92 |
93 | To execute commands on the car:
94 |
95 | 1. Use the `CarServer` or `Security` class to construct the appropriate command message.
96 | 2. Encapsulate the command message within a `RoutableMessage` using the `BuildRoutableMessage` function.
97 | 3. Send the resulting `RoutableMessage` to the car.
98 |
99 | ```c++
100 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size);
101 |
102 | size_t output_message_buffer_size = 0;
103 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size];
104 |
105 | session.BuildRoutableMessage(domain, action_message_buffer,
106 | action_message_buffer_size, output_message_buffer,
107 | &output_message_buffer_size);
108 | ```
109 |
110 | The `BuildRoutableMessage` function processes the message by encrypting its content, computing a checksum for integrity,
111 | and prepending the message length. The resulting output is a transmission-ready message formatted for the vehicle.
112 |
113 | ## Protocol Details
114 |
115 | For a deeper understanding of the protocol, please refer to the official
116 | documentation: [vehicle-command](https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md).
117 |
118 | ## Expanding Functionality
119 |
120 | While not all `VehicleAction` commands are currently implemented, adding new functionality is straightforward. Simply
121 | incorporate additional commands into the `carserver.cpp` class, following the established pattern of existing commands.
122 |
123 | ## Protocol Buffers (Protobuf)
124 |
125 | **Updating Protobuf Files:**
126 |
127 | 1. Download the latest Protobuf files from the Tesla vehicle-command project on GitHub.
128 | 2. Navigate to the library's root directory and execute the `proto.sh` shell script.
129 |
130 | **Important Considerations:**
131 |
132 | * Tesla may occasionally remove fields from messages, potentially causing compatibility issues. Remain vigilant for
133 | updates.
134 | * Advanced users can customize Protobuf generation through the `.options` files. Refer to the documentation for more
135 | information: [here](https://jpa.kapsi.fi/nanopb/docs/reference.html#generator-options)
136 |
137 | ### Troubleshooting Protobuf Generation Issues
138 |
139 | **Potential Causes:**
140 |
141 | * **Missing nanopb installation:** Ensure nanopb is installed correctly by running `pip install nanopb`.
142 | * **Incorrect PATH environment variable:** Verify that the `PATH` variable includes the directory containing
143 | the `protoc` executable. Use the following command to add it to your `.bashrc` file (adjust for zsh if necessary):
144 |
145 | ```bash
146 | echo "export PATH=\"`python3 -m site --user-base`/bin:\$PATH\"" >> ~/.bashrc
147 | source ~/.bashrc
148 | ```
149 |
150 | **Additional Tips:**
151 |
152 | * Consider using a virtual environment to isolate project dependencies.
153 | * Check for any conflicting installations of `protoc` or `nanopb`.
154 | * Verify that the protobuf files are formatted correctly.
155 | * Refer to the nanopb documentation for more in-depth troubleshooting guidance.
156 |
157 | ## Examples
158 |
159 | The library includes two example projects demonstrating direct car communication:
160 |
161 | ### Simple Example
162 |
163 | This example can be executed on a standard computer. To build and run:
164 |
165 | 1. Navigate to the `examples/simple` directory.
166 | 2. Create a build directory: `mkdir -p build`
167 | 3. Change to the build directory: `cd build`
168 | 4. Generate build files using CMake: `cmake ..`
169 | 5. Compile the project: `make`
170 |
171 | ### ESP32 Example
172 |
173 | This example requires a Seeed Studio XIAO ESP32C3 board and the PlatformIO development environment. Detailed
174 | instructions for setting up PlatformIO and configuring the project are available in the `examples/esp32` directory.
175 |
176 | ## Arduino Example: Automated Setup with `arduino.sh` Script
177 |
178 | **Automated Setup with `arduino.sh` Script**
179 |
180 | The Arduino example offers a convenient way to interact with your car, potentially through an automated setup script
181 | called `arduino.sh`. This script likely handles the following tasks:
182 |
183 | * **mbedtls Downgrade (Caution Advised):** Be aware that downgrading mbedTLS is not recommended due to security
184 | concerns. Consider exploring alternative approaches that support newer mbedTLS versions.
185 | * **Custom Library Creation:** The script might create a library file (`.zip`) compatible with the Arduino IDE. This
186 | library likely provides essential functionalities for communication with the car.
187 | * **Dependency Management:** It might manage the installation of additional dependencies such as nanopb version 0.4.8
188 | and the NimBLE library.
189 |
190 | **Important Considerations:**
191 |
192 | * **Security Risks:** Downgrading mbedTLS can expose your vehicle to security vulnerabilities. Prioritize secure
193 | solutions whenever possible.
194 |
195 | **Utilizing `arduino.sh` (if applicable):**
196 |
197 | 1. Ensure you understand the potential security risks associated with downgrading mbedTLS.
198 | 2. Review the script's functionality (if possible) to understand the steps it performs.
199 | 3. Execute the script according to its instructions (likely by running `./arduino.sh` in the terminal).
200 |
201 | ## License
202 |
203 | This project is dual-licensed under:
204 |
205 | 1. **GNU General Public License (AGPL) v3.0 or later**:
206 | Under this license, you are free to use, modify, and distribute the software, provided that any modifications or
207 | derivative works are also licensed under the GPL and the source code is made available.
208 |
209 | For the full text of the AGPL v3.0, see the [LICENSE](./LICENSE.md) file in this repository.
210 |
211 | 2. **Commercial License**:
212 | For organizations or individuals who wish to use this software in a proprietary product or without adhering to the
213 | terms of the AGPL, a commercial license is available.
214 |
215 | Please contact us at [me@pascal.sh](mailto://me@pascal.sh) for more information on purchasing a commercial
216 | license.
217 |
218 | ## Commercial License Information
219 |
220 | If you would like to use Tesla Vehicle Command (CPP/Arduino) in a commercial environment without the restrictions of the
221 | AGPL, you can purchase
222 | a commercial license. This license allows you to integrate Tesla Vehicle Command (CPP/Arduino) into your proprietary
223 | applications without the
224 | need to disclose your source code or comply with the AGPL's copyleft requirements.
225 |
226 | ## Contact
227 |
228 | For questions, support, or to inquire about a commercial license, please contact:
229 |
230 | - **Email**: [me@pascal.sh](mailto:me@pascal.sh)
231 | - **Website**: [https://pascal.sh](https://pascal.sh)
232 |
233 | # IMPORTANT
234 |
235 | Please take note that this library does not have official backing from Tesla, and its operational capabilities may be
236 | discontinued without prior notice. It's essential to recognize that this library retains private keys and other
237 | sensitive data on your device without encryption. I would like to stress that I assume no liability for any possible (
238 | although highly unlikely) harm that may befall your vehicle.
239 |
240 |
--------------------------------------------------------------------------------
/arduino.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | git apply --ignore-space-change --ignore-whitespace patches/mbedtls2.patch
4 | echo "updated code to run with mbedtls2"
5 |
6 | mkdir TeslaBLE
7 | cp src/* TeslaBLE/
8 | cp include/* TeslaBLE/
9 | zip -r library.zip TeslaBLE
10 | rm -rf TeslaBLE
11 |
12 | echo "created library.zip"
--------------------------------------------------------------------------------
/docs/metadata.md:
--------------------------------------------------------------------------------
1 | ## TeslaBLE::MetaData Class Documentation
2 |
3 | ### Overview
4 | The `TeslaBLE::MetaData` class is responsible for generating metadata for Tesla BLE communication. It utilizes the `mbedtls` library for SHA256 hashing and the `universal_message.pb.h` and `signatures.pb.h` protocol buffers for message structuring.
5 |
6 | ### Public Methods
7 |
8 | #### `int Start()`
9 | * **Purpose:** Initializes the metadata generation process.
10 | * **Returns:** An integer indicating success or failure.
11 |
12 | #### `int BuildMetadata(UniversalMessage_Domain destination, Signatures_SignatureType method, unsigned char *vin, uint32_t expiresAt, uint32_t counter, unsigned char *epoch)`
13 | * **Purpose:** Constructs metadata for a specific message.
14 | * **Parameters:**
15 | * `destination`: The destination domain of the message.
16 | * `method`: The signature method used for the message.
17 | * `vin`: The vehicle identification number (VIN).
18 | * `expiresAt`: The expiration timestamp of the metadata.
19 | * `counter`: A message counter.
20 | * `epoch`: An epoch value.
21 | * **Returns:** An integer indicating success or failure.
22 |
23 | #### `void Checksum(unsigned char *output, unsigned char end_tag)`
24 | * **Purpose:** Calculates the checksum of the generated metadata.
25 | * **Parameters:**
26 | * `output`: A buffer to store the calculated checksum.
27 | * `end_tag`: The ending tag for the checksum calculation.
28 |
29 | ### Private Members
30 |
31 | #### `mbedtls_sha256_context sha256_context_`
32 | * **Purpose:** Stores the SHA256 context for checksum calculation.
33 |
34 | #### `uint8_t last_tag`
35 | * **Purpose:** Tracks the last used tag for metadata construction.
36 |
37 | #### `int Add(unsigned char signatures_tag, unsigned char *value, unsigned char value_length)`
38 | * **Purpose:** Adds data to the metadata with a specific tag.
39 | * **Parameters:**
40 | * `signatures_tag`: The tag identifying the data.
41 | * `value`: The data to be added.
42 | * `value_length`: The length of the data.
43 | * **Returns:** An integer indicating success or failure.
44 |
45 | #### `int AddUint32(unsigned char signatures_tag, uint32_t value)`
46 | * **Purpose:** Adds a 32-bit unsigned integer to the metadata with a specific tag.
47 | * **Parameters:**
48 | * `signatures_tag`: The tag identifying the data.
49 | * `value`: The 32-bit unsigned integer value.
50 | * **Returns:** An integer indicating success or failure.
--------------------------------------------------------------------------------
/docs/security.md:
--------------------------------------------------------------------------------
1 | ## TeslaBLE::Security Class
2 |
3 | ### Overview
4 | The `TeslaBLE::Security` class encapsulates security-related operations within the TeslaBLE library. Based on the provided header, it appears to handle message encryption and decryption processes, likely using the `VCSEC_UnsignedMessage` structure defined in the `vcsec.pb.h` file.
5 |
6 | ### Public Methods
7 |
8 | #### `static int Unlock(unsigned char *buffer, size_t *buffer_size)`
9 | * **Purpose:** Decrypts the provided buffer.
10 | * **Parameters:**
11 | * `buffer`: The encrypted data buffer.
12 | * `buffer_size`: The size of the input buffer and output buffer.
13 | * **Returns:** An integer indicating success or failure.
14 |
15 | #### `static int Lock(unsigned char *buffer, size_t *buffer_size)`
16 | * **Purpose:** Encrypts the provided buffer.
17 | * **Parameters:**
18 | * `buffer`: The plaintext data buffer.
19 | * `buffer_size`: The size of the input buffer and output buffer.
20 | * **Returns:** An integer indicating success or failure.
21 |
22 | ### Private Methods
23 | #### `static int BuildUnsignedMessage(const VCSEC_UnsignedMessage *unsigned_message, unsigned char *buffer, size_t *buffer_size)`
24 | * **Purpose:** Builds an unsigned message based on the provided `VCSEC_UnsignedMessage` structure.
25 | * **Parameters:**
26 | * `unsigned_message`: The unsigned message structure.
27 | * `buffer`: The output buffer to hold the encoded message.
28 | * `buffer_size`: The size of the output buffer.
29 | * **Returns:** An integer indicating success or failure.
30 |
--------------------------------------------------------------------------------
/docs/session.md:
--------------------------------------------------------------------------------
1 | ## TeslaBLE::Session Class
2 |
3 | ### Overview
4 | The `TeslaBLE::Session` class appears to manage session-related data and operations for Tesla BLE communication. It handles session information, metadata generation, authentication, and message construction.
5 |
6 | ### Key Components and Responsibilities
7 | * **Session Data:** Manages session-specific data for different domains, including time zeros, counters, epochs, clock times, car keys, and car key sizes.
8 | * **Metadata:** Instantiates a `MetaData` object for generating metadata.
9 | * **Authentication:** Collaborates with an `Authenticator` object for authentication tasks.
10 | * **Message Construction:** Builds routable and action messages, as well as request session info messages.
11 |
12 | ### Public Methods
13 |
14 | * **LoadAuthenticator:** Sets the `authenticator_` member to the provided `Authenticator` object.
15 | * **LoadPrivateKey:** Loads a private key for authentication purposes.
16 | * **LoadPrivateKeyContext:** Loads a pre-initialized MbedTLS private key context.
17 | * **GenerateRoutingAddress:** Generates a routing address.
18 | * **SetRoutingAddress:** Sets the routing address manually.
19 | * **UpdateSessionInfo:** Updates session information for a specific domain.
20 | * **BuildRoutableMessage:** Builds a routable message for a given domain and action message.
21 | * **BuildActionMessage:** Builds an action message based on a `CarServer_VehicleAction` structure.
22 | * **BuildRequestSessionInfoMessage:** Builds a request session info message for a given domain.
23 | * **ExpiresAt:** Calculates the expiration time for a given domain and expiration time in seconds.
24 | * **Counter:** Returns the counter for a given domain.
25 | * **Epoch:** Returns the epoch for a given domain.
26 | * **SetVIN:** Sets the vehicle identification number (VIN).
27 | * **ExportSessionInfo:** Exports session information for a given domain.
28 |
--------------------------------------------------------------------------------
/docs/shared.md:
--------------------------------------------------------------------------------
1 | ## TeslaBLE::Shared Header File
2 |
3 | ### Overview
4 | The `shared.h` header file provides common definitions, enums, and utility functions used across the TeslaBLE library. It serves as a central repository for shared data types and helper functions.
5 |
6 | ### Detailed Breakdown
7 |
8 | #### Includes
9 | * Includes necessary header files for standard data types (`cstdint`, `cstddef`), protocol buffer definitions (`universal_message.pb.h`, `vcsec.pb.h`), and possibly other shared definitions (`shared.h`).
10 |
11 | #### ResultCode Enum
12 | * Defines a set of error codes used throughout the library for indicating success or failure of various operations.
13 | * `SUCCESS`: Operation successful.
14 | * `ERROR`: Generic error.
15 | * `TAG_OUT_OF_ORDER`: Tag used in metadata is out of order.
16 | * `TAG_VALUE_EMPTY`: Tag value is empty.
17 | * `TAG_VALUE_TOO_LONG`: Tag value exceeds maximum length.
18 | * `MBEDTLS_ERROR`: Error occurred in MbedTLS library.
19 | * `NANOPB_ENCODE_ERROR`: Error occurred during NanoPB encoding.
20 | * `NANOPB_DECODE_ERROR`: Error occurred during NanoPB decoding.
21 | * `PRIVATE_KEY_NOT_LOADED`: Private key is not loaded.
22 | * `SESSION_INFO_NOT_LOADED`: Session information is not loaded.
23 | * `SESSION_INFO_KEY_NOT_WHITELISTED`: Session info key is not whitelisted.
24 |
25 | #### Common Class
26 | * Provides static utility functions for various operations.
27 | * `HexStrToUint8`: Converts a hexadecimal string to a byte array.
28 | * `DumpHexBuffer`: Prints a hexadecimal representation of a byte buffer to the console for debugging purposes.
29 | * `PrependLength`: Prepends the length of an input buffer to an output buffer.
30 | * `calculateIdentifier`: Calculates an identifier based on a VIN.
31 | * `ExtractLength`: Extracts the length from an input buffer.
32 | * `GenerateUUID`: Generates a UUID.
33 | * `DecodeRoutableMessage`: Decodes a routable message from a byte buffer using NanoPB.
34 | * `DecodeFromVCSECMessage`: Decodes a VCSEC message from a byte buffer using NanoPB.
35 | * `EncodeRoutableMessage`: Encodes a routable message to a byte buffer using NanoPB.
36 | * `ResultCodeToMessage`: Converts a result code to a human-readable message.
37 | * `ErrorCodeToMessage`: Converts a message fault enum to a human-readable message.
38 | * `OperationStatusToMessage`: Converts an operation status enum to a human-readable message.
39 | * `PrintErrorFromMbedTlsErrorCode`: Prints an error message based on an MbedTLS error code.
40 |
--------------------------------------------------------------------------------
/examples/arduino/arduino.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "NimBLEDevice.h"
8 | #include
9 |
10 | static NimBLEUUID serviceUUID("00000211-b2d1-43f0-9b88-960cebf8b91e");
11 | static NimBLEUUID readUUID("00000213-b2d1-43f0-9b88-960cebf8b91e");
12 | static NimBLEUUID writeUUID("00000212-b2d1-43f0-9b88-960cebf8b91e");
13 |
14 | static NimBLEAddress car("b0:d2:78:87:26:72");
15 |
16 | static NimBLERemoteCharacteristic *readCharacteristic;
17 | static NimBLERemoteCharacteristic *writeCharacteristic;
18 |
19 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{};
20 | TeslaBLE::Session session = TeslaBLE::Session{};
21 |
22 | static boolean doConnect = true;
23 | unsigned char ble_buffer[200];
24 | size_t current_message_size = 0;
25 |
26 | size_t action_message_buffer_size = 0;
27 | unsigned char action_message_buffer[50];
28 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
29 |
30 | size_t output_message_buffer_size = 0;
31 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size];
32 |
33 |
34 | void setup() {
35 | Serial.begin(9600);
36 | NimBLEDevice::init("TeslaBLE");
37 |
38 | const char *vin = "XP7YGCEL0NB000000";
39 | unsigned char private_key[227] =
40 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----";
41 |
42 | authenticator.LoadPrivateKey(private_key, sizeof private_key);
43 |
44 | session.SetVIN((unsigned char*)vin);
45 | session.GenerateRoutingAddress();
46 | session.LoadAuthenticator(&authenticator);
47 | }
48 |
49 |
50 | void handleMessage() {
51 | TeslaBLE::Common::DumpHexBuffer("RX: ", ble_buffer, current_message_size);
52 |
53 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_zero;
54 | TeslaBLE::Common::DecodeRoutableMessage(ble_buffer, current_message_size, &routable_message);
55 |
56 | current_message_size = 0;
57 |
58 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination.
59 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) {
60 | // printf("Dropping broadcast message\n");
61 |
62 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default;
63 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes,
64 | routable_message.payload.protobuf_message_as_bytes.size,
65 | &vcsec_from_vcsec_message);
66 |
67 | Serial.printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message);
68 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) {
69 | Serial.printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence);
70 | Serial.printf("Charging Port Open: %d\n",
71 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort);
72 | }
73 |
74 | return;
75 | }
76 |
77 | if (routable_message.has_signedMessageStatus) {
78 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault);
79 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status);
80 | return;
81 | }
82 |
83 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) {
84 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination.
85 | domain,
86 | routable_message.payload.session_info.bytes,
87 | routable_message.payload.session_info.size);
88 | }
89 | }
90 |
91 | void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData,
92 | size_t length, bool isNotify)
93 | {
94 | unsigned char input_buffer[length];
95 | memcpy(&input_buffer, pData, length);
96 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer);
97 |
98 | if (current_message_size == 0 && size > length) {
99 | current_message_size = 0;
100 |
101 | size_t payload_size = length - 2;
102 | memcpy(ble_buffer, input_buffer + 2, payload_size);
103 | current_message_size = payload_size;
104 | return;
105 | }
106 |
107 | if (current_message_size > 0) {
108 | memcpy(ble_buffer + current_message_size, input_buffer, length);
109 | current_message_size = current_message_size + length;
110 | handleMessage();
111 | return;
112 | }
113 |
114 | size_t payload_size = length - 2;
115 | memcpy(ble_buffer, input_buffer + 2, payload_size);
116 | current_message_size = payload_size;
117 | handleMessage();
118 | }
119 |
120 |
121 | bool connectToCar()
122 | {
123 | Serial.printf("Connecting to car!\n");
124 |
125 | BLEClient *pClient = BLEDevice::createClient();
126 | pClient->connect(car);
127 |
128 | if (!pClient->isConnected())
129 | {
130 | Serial.printf("Failed to connect!\n");
131 | return false;
132 | }
133 |
134 | Serial.printf("Connected to car!\n");
135 |
136 | BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
137 | if (pRemoteService == nullptr)
138 | {
139 | Serial.printf("Failed to find our service UUID: %s\n",
140 | serviceUUID.toString().c_str());
141 | pClient->disconnect();
142 | return false;
143 | }
144 |
145 | readCharacteristic = pRemoteService->getCharacteristic(readUUID);
146 | if (readCharacteristic == nullptr)
147 | {
148 | Serial.printf("Failed to find our read characteristic UUID: %s\n",
149 | readUUID.toString().c_str());
150 | pClient->disconnect();
151 | return false;
152 | }
153 |
154 | if (!readCharacteristic->subscribe(false, notifyCB))
155 | {
156 | Serial.printf("Failed to subscribe to characteristic UUID: %s\n",
157 | readUUID.toString().c_str());
158 | return false;
159 | }
160 |
161 | writeCharacteristic = pRemoteService->getCharacteristic(writeUUID);
162 | if (writeCharacteristic == nullptr)
163 | {
164 | Serial.printf("Failed to find our write characteristic UUID: %s\n",
165 | writeUUID.toString().c_str());
166 | pClient->disconnect();
167 | return false;
168 | }
169 |
170 | return true;
171 | }
172 |
173 | void loop() {
174 | if (doConnect) {
175 | if (connectToCar()) {
176 | Serial.printf("We are now connected to the Car.\n");
177 |
178 | unsigned char sessionInfoRequestBuffer[200];
179 | size_t sessionInfoRequestBufferLength = 0;
180 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT,
181 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength);
182 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength);
183 |
184 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY,
185 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength);
186 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength);
187 | doConnect = false;
188 | } else {
189 | Serial.printf("We have failed to connect to the server; there is nothin more we will do.\n");
190 | }
191 | } else {
192 | Serial.printf("\nclimate-on: Climate ON\nclimate-ff: Climate OFF\nmedia-next: Media Next Track\nlock: Lock\nunlock: UnLock\n\nPlease select a command: ");
193 |
194 | while (Serial.available() == 0) {}
195 | String input = Serial.readString();
196 | input.trim();
197 |
198 | if (input == "climate-on") {
199 | Serial.printf("Sending climate on command\n");
200 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
201 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size);
202 | } else if (input == "climate-off") {
203 | Serial.printf("Sending climate off command\n");
204 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
205 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size);
206 | } else if (input == "media-next") {
207 | Serial.printf("Sending media next track command\n");
208 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
209 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size);
210 | } else if (input == "lock") {
211 | Serial.printf("Sending lock command\n");
212 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
213 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size);
214 | } else if (input == "unlock") {
215 | Serial.printf("Sending unlock command\n");
216 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
217 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size);
218 | } else {
219 | action_message_buffer_size = 0;
220 | Serial.printf("Please select a valid command.\n");
221 | }
222 |
223 | if (action_message_buffer_size > 0) {
224 | session.BuildRoutableMessage(domain, action_message_buffer,
225 | action_message_buffer_size, output_message_buffer,
226 | &output_message_buffer_size);
227 |
228 | writeCharacteristic->writeValue(output_message_buffer, output_message_buffer_size);
229 |
230 | action_message_buffer_size = 0;
231 | output_message_buffer_size = 0;
232 | }
233 | }
234 | }
--------------------------------------------------------------------------------
/examples/esp32/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 |
--------------------------------------------------------------------------------
/examples/esp32/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/examples/esp32/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.16.0)
2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake)
3 | project(BLETesla)
4 |
--------------------------------------------------------------------------------
/examples/esp32/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env:seeed_xiao_esp32c3]
12 | platform = espressif32
13 | board = seeed_xiao_esp32c3
14 | framework = espidf
15 | lib_deps =
16 | nanopb/Nanopb@^0.4.8
17 | https://github.com/h2zero/esp-nimble-cpp.git#3c5a2fd4a9c327e87f9b5cdcf9b7382f66bc11be
18 | symlink://../../
19 |
--------------------------------------------------------------------------------
/examples/esp32/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file was automatically generated for projects
2 | # without default 'CMakeLists.txt' file.
3 |
4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
5 |
6 | idf_component_register(SRCS ${app_sources})
7 |
--------------------------------------------------------------------------------
/examples/esp32/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "esp_system.h"
6 | #include "freertos/FreeRTOS.h"
7 | #include "freertos/task.h"
8 | #include
9 | #include
10 | #include
11 | #include "NimBLEDevice.h"
12 | #include
13 |
14 | static NimBLEUUID serviceUUID("00000211-b2d1-43f0-9b88-960cebf8b91e");
15 | static NimBLEUUID readUUID("00000213-b2d1-43f0-9b88-960cebf8b91e");
16 | static NimBLEUUID writeUUID("00000212-b2d1-43f0-9b88-960cebf8b91e");
17 |
18 | static NimBLERemoteCharacteristic *readCharacteristic;
19 | static NimBLERemoteCharacteristic *writeCharacteristic;
20 |
21 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{};
22 | TeslaBLE::Session session = TeslaBLE::Session{};
23 |
24 | unsigned char ble_buffer[200];
25 | size_t current_message_size = 0;
26 |
27 | extern "C"
28 | {
29 | void app_main(void);
30 | }
31 |
32 | void readInput(void *pvParameter)
33 | {
34 | uint8_t rxbuf[1];
35 |
36 | printf("\n1: Climate ON\n2: Climate OFF\n3: Media Next Track\n4: Lock\n5: UnLock\n\nPlease select a command: ");
37 | size_t action_message_buffer_size = 0;
38 | unsigned char action_message_buffer[50];
39 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
40 |
41 | size_t output_message_buffer_size = 0;
42 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size];
43 |
44 | while (1)
45 | {
46 | if (usb_serial_jtag_ll_rxfifo_data_available())
47 | {
48 | usb_serial_jtag_ll_read_rxfifo(rxbuf, 1);
49 |
50 | switch (rxbuf[0])
51 | {
52 | case 49:
53 | printf("Sending climate on command\n");
54 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
55 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size);
56 | break;
57 | case 50:
58 | printf("Sending climate off command\n");
59 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
60 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size);
61 | break;
62 | case 51:
63 | printf("Sending media next track command\n");
64 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
65 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size);
66 | break;
67 | case 52:
68 | printf("Sending lock command\n");
69 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
70 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size);
71 | break;
72 | case 53:
73 | printf("Sending unlock command\n");
74 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
75 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size);
76 | break;
77 | default:
78 | action_message_buffer_size = 0;
79 | printf("Please select a valid command.\n");
80 | break;
81 | }
82 |
83 | if (action_message_buffer_size > 0)
84 | {
85 | session.BuildRoutableMessage(domain, action_message_buffer,
86 | action_message_buffer_size, output_message_buffer,
87 | &output_message_buffer_size);
88 |
89 | writeCharacteristic->writeValue(output_message_buffer, output_message_buffer_size);
90 |
91 | action_message_buffer_size = 0;
92 | output_message_buffer_size = 0;
93 | }
94 | }
95 |
96 | vTaskDelay(pdMS_TO_TICKS(10));
97 | }
98 |
99 | free(rxbuf);
100 | vTaskDelete(NULL);
101 | }
102 |
103 | void handleMessage() {
104 | TeslaBLE::Common::DumpHexBuffer("RX: ", ble_buffer, current_message_size);
105 |
106 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_zero;
107 | TeslaBLE::Common::DecodeRoutableMessage(ble_buffer, current_message_size, &routable_message);
108 |
109 | current_message_size = 0;
110 |
111 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination.
112 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) {
113 | // printf("Dropping broadcast message\n");
114 |
115 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default;
116 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes,
117 | routable_message.payload.protobuf_message_as_bytes.size,
118 | &vcsec_from_vcsec_message);
119 |
120 | printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message);
121 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) {
122 | printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence);
123 | printf("Charging Port Open: %d\n",
124 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort);
125 | }
126 |
127 | return;
128 | }
129 |
130 | if (routable_message.has_signedMessageStatus) {
131 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault);
132 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status);
133 | return;
134 | }
135 |
136 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) {
137 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination.
138 | domain,
139 | routable_message.payload.session_info.bytes,
140 | routable_message.payload.session_info.size);
141 | }
142 | }
143 |
144 | void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData,
145 | size_t length, bool isNotify)
146 | {
147 | unsigned char input_buffer[length];
148 | memcpy(&input_buffer, pData, length);
149 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer);
150 |
151 | if (current_message_size == 0 && size > length) {
152 | current_message_size = 0;
153 |
154 | size_t payload_size = length - 2;
155 | memcpy(ble_buffer, input_buffer + 2, payload_size);
156 | current_message_size = payload_size;
157 | return;
158 | }
159 |
160 | if (current_message_size > 0) {
161 | memcpy(ble_buffer + current_message_size, input_buffer, length);
162 | current_message_size = current_message_size + length;
163 | handleMessage();
164 | return;
165 | }
166 |
167 | size_t payload_size = length - 2;
168 | memcpy(ble_buffer, input_buffer + 2, payload_size);
169 | current_message_size = payload_size;
170 | handleMessage();
171 | }
172 |
173 | bool connectToCar()
174 | {
175 | printf("Connect to car!\n");
176 |
177 | BLEClient *pClient = BLEDevice::createClient();
178 | pClient->connect(NimBLEAddress("b0:d2:78:87:26:72"));
179 |
180 | if (!pClient->isConnected())
181 | {
182 | printf("Failed to connect!\n");
183 | return false;
184 | }
185 |
186 | printf("Connected to car!\n");
187 |
188 | BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
189 | if (pRemoteService == nullptr)
190 | {
191 | printf("Failed to find our service UUID: %s\n",
192 | serviceUUID.toString().c_str());
193 | pClient->disconnect();
194 | return false;
195 | }
196 |
197 | readCharacteristic = pRemoteService->getCharacteristic(readUUID);
198 | if (readCharacteristic == nullptr)
199 | {
200 | printf("Failed to find our read characteristic UUID: %s\n",
201 | readUUID.toString().c_str());
202 | pClient->disconnect();
203 | return false;
204 | }
205 |
206 | if (!readCharacteristic->subscribe(false, notifyCB))
207 | {
208 | printf("Failed to subscribe to characteristic UUID: %s\n",
209 | readUUID.toString().c_str());
210 | return false;
211 | }
212 |
213 | writeCharacteristic = pRemoteService->getCharacteristic(writeUUID);
214 | if (writeCharacteristic == nullptr)
215 | {
216 | printf("Failed to find our write characteristic UUID: %s\n",
217 | writeUUID.toString().c_str());
218 | pClient->disconnect();
219 | return false;
220 | }
221 |
222 | return true;
223 | }
224 |
225 | void connectTask(void *parameter)
226 | {
227 | if (connectToCar())
228 | {
229 | printf("We are now connected to the Car.\n");
230 |
231 | unsigned char sessionInfoRequestBuffer[200];
232 | size_t sessionInfoRequestBufferLength = 0;
233 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT,
234 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength);
235 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength);
236 |
237 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY,
238 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength);
239 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength);
240 |
241 | xTaskCreate(readInput, "readInput", 10000, NULL, 1, NULL);
242 | }
243 | else
244 | {
245 | printf("We have failed to connect to the server; there is nothin more we will do.\n");
246 | }
247 |
248 | vTaskDelete(NULL);
249 | }
250 |
251 | void app_main()
252 | {
253 | NimBLEDevice::init("TeslaBLE");
254 |
255 | const char *vin = "XP7YGCEL0NB000000";
256 | unsigned char private_key[227] =
257 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----";
258 |
259 | authenticator.LoadPrivateKey(private_key, sizeof private_key);
260 |
261 | session.SetVIN((unsigned char*)vin);
262 | session.GenerateRoutingAddress();
263 | session.LoadAuthenticator(&authenticator);
264 |
265 | xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
266 | }
--------------------------------------------------------------------------------
/examples/simple/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.22)
2 | project(simple)
3 |
4 | include(FetchContent)
5 | FetchContent_Declare(
6 | simpleble
7 | GIT_REPOSITORY https://github.com/OpenBluetoothToolbox/SimpleBLE.git
8 | GIT_TAG v0.7.3
9 | GIT_SHALLOW YES
10 | )
11 |
12 | set(CMAKE_CXX_STANDARD 17)
13 |
14 | FetchContent_GetProperties(simpleble)
15 | if (NOT simpleble_POPULATED)
16 | FetchContent_Populate(simpleble)
17 | list(APPEND CMAKE_MODULE_PATH "${simpleble_SOURCE_DIR}/cmake/find")
18 | add_subdirectory("${simpleble_SOURCE_DIR}/simpleble" "${simpleble_BINARY_DIR}")
19 | endif ()
20 |
21 | set(simpleble_FOUND 1)
22 |
23 | add_subdirectory(../../ TeslaBLE)
24 | add_executable(bluetooth ble.cpp src/ble.cpp)
25 | target_link_libraries(bluetooth PRIVATE TeslaBLE simpleble::simpleble)
26 | target_include_directories(bluetooth PRIVATE include/)
27 |
28 |
--------------------------------------------------------------------------------
/examples/simple/ble.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "ble.h"
12 |
13 | TeslaBLE::BLE ble = TeslaBLE::BLE(false);
14 | TeslaBLE::Session session = TeslaBLE::Session{};
15 |
16 | void message_handler(UniversalMessage_RoutableMessage routable_message) {
17 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination.
18 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) {
19 | // printf("Dropping broadcast message\n");
20 |
21 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default;
22 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes,
23 | routable_message.payload.protobuf_message_as_bytes.size,
24 | &vcsec_from_vcsec_message);
25 |
26 | // printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message);
27 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) {
28 | printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence);
29 | printf("Charging Port Open: %d\n",
30 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort);
31 | }
32 |
33 | return;
34 | }
35 |
36 | if (routable_message.has_signedMessageStatus) {
37 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault);
38 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status);
39 | return;
40 | }
41 |
42 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) {
43 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination.
44 | domain,
45 | routable_message.payload.session_info.bytes,
46 | routable_message.payload.session_info.size);
47 | }
48 | }
49 |
50 | int main() {
51 | const char *vin = "XP7YGCEL0NB000000";
52 | unsigned char private_key[227] =
53 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----";
54 |
55 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{};
56 | authenticator.LoadPrivateKey(private_key, sizeof private_key);
57 |
58 | ble.registerMessageHandler(message_handler);
59 | int result = ble.connect((unsigned char *) vin);
60 | if (result != 0) {
61 | printf("Failed to connect to the vehicle.\n");
62 | return 1;
63 | }
64 |
65 | session.SetVIN((unsigned char *) vin);
66 | session.GenerateRoutingAddress();
67 | session.LoadAuthenticator(&authenticator);
68 |
69 | unsigned char sessionInfoRequestBuffer[200];
70 | size_t sessionInfoRequestBufferLength = 0;
71 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT,
72 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength);
73 |
74 | unsigned char securitySessionInfoRequestBuffer[200];
75 | size_t securitySessionInfoRequestBufferLength = 0;
76 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY,
77 | securitySessionInfoRequestBuffer,
78 | &securitySessionInfoRequestBufferLength);
79 |
80 |
81 | //authenticator.CreatePrivateKey();
82 | //authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, sessionInfoRequestBuffer,
83 | // &sessionInfoRequestBufferLength);
84 |
85 | int userInput;
86 | bool runLoop = true;
87 | bool hasUpdatedSessionInfo = false;
88 |
89 | // Prompt the user to enter a number
90 | std::cout << "\n\n1: Whitelist Private Key\n";
91 | std::cout << "2: Update SessionInfo\n";
92 | std::cout << "3: Climate ON\n";
93 | std::cout << "4: Climate OFF\n";
94 | std::cout << "5: Media Next Track\n";
95 | std::cout << "6: Lock\n";
96 | std::cout << "7: UnLock\n";
97 | std::cout << "8: Disconnect\n";
98 | std::cout << "9: Export VEHICLE_SECURITY SessionInfo\n";
99 | std::cout << "10: Export DOMAIN_INFOTAINMENT SessionInfo\n\n";
100 |
101 | while (runLoop) {
102 | std::cout << "Please select a command: ";
103 | std::cin >> userInput;
104 |
105 | size_t action_message_buffer_size = 0;
106 | unsigned char action_message_buffer[50];
107 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
108 |
109 | size_t session_size = 0;
110 | unsigned char session_buffer[Signatures_SessionInfo_size];
111 |
112 | size_t whitelist_size = 0;
113 | unsigned char whitelist_buffer[UniversalMessage_RoutableMessage_size];
114 |
115 | if (!hasUpdatedSessionInfo && userInput != 2 && userInput != 1) {
116 | std::cout << "\n\n\nYou have to execute command 2: to update the SessionInfo!\n\n\n";
117 | }
118 |
119 | std::cout << "\n";
120 | switch (userInput) {
121 | case 1:
122 | authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, whitelist_buffer, &whitelist_size);
123 | std::cout << "\n\n\nTouch NFC Card now!\n\n\n";
124 | break;
125 | case 2:
126 | ble.send((char *) sessionInfoRequestBuffer, sessionInfoRequestBufferLength);
127 | ble.send((char *) securitySessionInfoRequestBuffer, securitySessionInfoRequestBufferLength);
128 | hasUpdatedSessionInfo = true;
129 | std::cout << "SessionInfo updated\n";
130 | break;
131 | case 3:
132 | std::cout << "Sending climate on command\n";
133 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
134 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size);
135 | break;
136 | case 4:
137 | std::cout << "Sending climate off command\n";
138 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
139 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size);
140 | break;
141 | case 5:
142 | std::cout << "Sending media next track command\n";
143 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT;
144 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size);
145 | break;
146 | case 6:
147 | std::cout << "Sending media next track command\n";
148 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
149 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size);
150 | break;
151 | case 7:
152 | std::cout << "Sending media next track command\n";
153 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY;
154 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size);
155 | break;
156 | case 8:
157 | ble.close();
158 | runLoop = false;
159 | break;
160 | case 9:
161 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_buffer,
162 | &session_size);
163 | TeslaBLE::Common::DumpHexBuffer("SessionInfo: ", session_buffer, session_size);
164 | break;
165 | case 10:
166 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, session_buffer,
167 | &session_size);
168 | TeslaBLE::Common::DumpHexBuffer("SessionInfo: ", session_buffer, session_size);
169 | break;
170 | default:
171 | std::cout << "Please select a valid command.\n";
172 | break;
173 | }
174 |
175 | if (whitelist_size > 0) {
176 | ble.send((char *) whitelist_buffer, whitelist_size);
177 | whitelist_size = 0;
178 | }
179 |
180 | if (action_message_buffer_size > 0) {
181 | size_t output_message_buffer_size = 0;
182 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size];
183 |
184 | session.BuildRoutableMessage(domain, action_message_buffer,
185 | action_message_buffer_size, output_message_buffer,
186 | &output_message_buffer_size);
187 |
188 | ble.send((char *) output_message_buffer, output_message_buffer_size);
189 | action_message_buffer_size = 0;
190 | }
191 | }
192 |
193 | return 0;
194 | }
195 |
--------------------------------------------------------------------------------
/examples/simple/include/ble.h:
--------------------------------------------------------------------------------
1 | #ifndef BLE_H
2 | #define BLE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace TeslaBLE {
10 | class BLE {
11 | private:
12 | size_t MAX_MESSAGE_SIZE = 1024;
13 | size_t current_message_size = 0;
14 | std::vector message_buffer;
15 | std::function message_handler;
16 | SimpleBLE::Peripheral peripheral;
17 | bool debug_enabled = false;
18 |
19 | void handleMessage(std::vector message);
20 |
21 | void callback(SimpleBLE::ByteArray payload);
22 |
23 | public:
24 | explicit BLE(bool debug_enabled) {
25 | this->debug_enabled = debug_enabled;
26 | }
27 |
28 | SimpleBLE::Peripheral search(const unsigned char *vin);
29 |
30 | int connect(unsigned char *vin);
31 |
32 | void registerMessageHandler(std::function message_handler);
33 |
34 | void send(char *buffer, size_t buffer_size);
35 |
36 | void close();
37 | };
38 | } // TeslaBLE
39 |
40 | #endif //BLE_H
41 |
--------------------------------------------------------------------------------
/examples/simple/src/ble.cpp:
--------------------------------------------------------------------------------
1 | #include "ble.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "pb_decode.h"
9 |
10 | auto CAR_SERVICE_UUID = "00000211-b2d1-43f0-9b88-960cebf8b91e";
11 | auto CAR_WRITE_CHAR_UUID = "00000212-b2d1-43f0-9b88-960cebf8b91e";
12 | auto CAR_READ_CHAR_UUID = "00000213-b2d1-43f0-9b88-960cebf8b91e";
13 |
14 | namespace TeslaBLE {
15 | void BLE::handleMessage(std::vector message) {
16 | if (this->debug_enabled) {
17 | TeslaBLE::Common::DumpHexBuffer("RX: ", message.data(), message.size());
18 | }
19 |
20 | UniversalMessage_RoutableMessage output_message = UniversalMessage_RoutableMessage_init_zero;
21 | Common::DecodeRoutableMessage(message.data(), message.size(), &output_message);
22 |
23 | current_message_size = 0;
24 | this->message_buffer.clear();
25 | this->message_buffer.resize(0);
26 | this->message_buffer.shrink_to_fit();
27 |
28 | this->message_handler(output_message);
29 | }
30 |
31 | void BLE::callback(SimpleBLE::ByteArray payload) {
32 | pb_byte_t input_buffer[payload.size()];
33 | memcpy(input_buffer, payload.data(), payload.size());
34 |
35 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer);
36 | if (current_message_size == 0 && size > payload.size()) {
37 | message_buffer.clear();
38 | message_buffer.resize(size);
39 | message_buffer.shrink_to_fit();
40 | current_message_size = 0;
41 |
42 | size_t payload_size = payload.size() - 2;
43 | memcpy(message_buffer.data(), input_buffer + 2, payload_size);
44 | current_message_size = payload_size;
45 | return;
46 | }
47 |
48 | if (current_message_size > 0) {
49 | memcpy(message_buffer.data() + current_message_size, input_buffer, payload.size());
50 | current_message_size = current_message_size + payload.size();
51 | this->handleMessage(message_buffer);
52 | return;
53 | }
54 |
55 | message_buffer.clear();
56 | message_buffer.resize(size);
57 | message_buffer.shrink_to_fit();
58 | current_message_size = 0;
59 |
60 | size_t payload_size = payload.size() - 2;
61 | memcpy(message_buffer.data(), input_buffer + 2, payload_size);
62 | this->handleMessage(message_buffer);
63 | }
64 |
65 | int BLE::connect(unsigned char *vin) {
66 | const auto adapters = SimpleBLE::Adapter::get_adapters();
67 | auto adapter = adapters[0];
68 |
69 | printf("Using adapter MAC: %s\n", adapter.address().c_str());
70 |
71 | char identifier[21];
72 | int result = TeslaBLE::Common::calculateIdentifier(vin, identifier);
73 | if (result != ResultCode::SUCCESS) {
74 | return result;
75 | }
76 |
77 | printf("Connecting to Tesla (%s)...\n", identifier);
78 | adapter.scan_for(5000);
79 | auto peripherals = adapter.scan_get_results();
80 |
81 | for (auto peripheral: peripherals) {
82 | if (peripheral.identifier() == identifier) {
83 | peripheral.connect();
84 | printf("Found Tesla \"%s\"\n", peripheral.identifier().c_str());
85 | printf("MAC: %s", peripheral.address().c_str());
86 | peripheral.indicate(CAR_SERVICE_UUID, CAR_READ_CHAR_UUID, [this](SimpleBLE::ByteArray payload) {
87 | this->callback(payload);
88 | });
89 | this->peripheral = peripheral;
90 | return 0;
91 | }
92 | }
93 |
94 | printf("Found Tesla not found \"%s\"\n", identifier);
95 | return 1;
96 | }
97 |
98 | void BLE::registerMessageHandler(std::function message_handler) {
99 | this->message_handler = std::move(message_handler);
100 | }
101 |
102 | void BLE::send(char *buffer, size_t buffer_size) {
103 | SimpleBLE::ByteArray payload = SimpleBLE::ByteArray((char *) buffer, buffer_size);
104 | this->peripheral.write_request(CAR_SERVICE_UUID, CAR_WRITE_CHAR_UUID, payload);
105 | }
106 |
107 | void BLE::close() {
108 | this->peripheral.unsubscribe(CAR_SERVICE_UUID, CAR_READ_CHAR_UUID);
109 | this->peripheral.disconnect();
110 | }
111 | } // TeslaBLE
112 |
--------------------------------------------------------------------------------
/include/authenticator.h:
--------------------------------------------------------------------------------
1 | /*
2 | * TeslaBLE © 2024 by Pascal Matthiesen
3 | */
4 |
5 | #ifndef TESLA_BLE_AUTHENTICATOR_H_INCLUDED
6 | #define TESLA_BLE_AUTHENTICATOR_H_INCLUDED
7 |
8 | #ifdef ESP_PLATFORM
9 | #define MBEDTLS_CONFIG_FILE "mbedtls/esp_config.h"
10 | #endif
11 |
12 | #include
13 | #include
14 | #include