1 // Copyright 2022 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://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, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <memory> 17 #include <optional> 18 19 #include "pw_bluetooth/internal/raii_ptr.h" 20 #include "pw_bluetooth/low_energy/advertising_data.h" 21 #include "pw_bluetooth/low_energy/connection.h" 22 #include "pw_bluetooth/result.h" 23 #include "pw_bluetooth/types.h" 24 #include "pw_chrono/system_clock.h" 25 #include "pw_containers/vector.h" 26 #include "pw_function/function.h" 27 28 namespace pw::bluetooth::low_energy { 29 30 /// Represents the LE central role. Used to scan and connect to peripherals. 31 class Central { 32 public: 33 /// Represents an ongoing LE scan. 34 class ScanHandle { 35 public: 36 /// Possible errors that can cause a scan to stop prematurely. 37 enum class ScanError : uint8_t { kCanceled = 0 }; 38 39 virtual ~ScanHandle() = 0; 40 41 /// Set a callback that will be called if the scan is stopped due to an 42 /// error in the BLE stack. 43 virtual void SetErrorCallback(Function<void(ScanError)>&& callback) = 0; 44 45 private: 46 /// Stop the current scan. This method is called by the ~ScanHandle::Ptr() 47 /// when it goes out of scope, the API client should never call this method. 48 virtual void StopScan() = 0; 49 50 public: 51 /// Movable ScanHandle smart pointer. The controller will continue scanning 52 /// until the ScanHandle::Ptr is destroyed. 53 using Ptr = internal::RaiiPtr<ScanHandle, &ScanHandle::StopScan>; 54 }; 55 56 /// Filter parameters for use during a scan. A discovered peer only matches 57 /// the filter if it satisfies all of the present filter parameters. 58 struct ScanFilter { 59 /// Filter based on advertised service UUID. 60 std::optional<Uuid> service_uuid; 61 62 /// Filter based on service data containing the given UUID. 63 std::optional<Uuid> service_data_uuid; 64 65 /// Filter based on a manufacturer identifier present in the manufacturer 66 /// data. If this filter parameter is set, then the advertising payload must 67 /// contain manufacturer specific data with the provided company identifier 68 /// to satisfy this filter. Manufacturer identifiers can be found at 69 /// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/ 70 std::optional<uint16_t> manufacturer_id; 71 72 /// Filter based on whether or not a device is connectable. For example, a 73 /// client that is only interested in peripherals that it can connect to can 74 /// set this to true. Similarly a client can scan only for broadcasters by 75 /// setting this to false. 76 std::optional<bool> connectable; 77 78 /// Filter results based on a portion of the advertised device name. 79 /// Substring matches are allowed. 80 /// The name length must be at most pw::bluetooth::kMaxDeviceNameLength. 81 std::optional<std::string_view> name; 82 83 /// Filter results based on the path loss of the radio wave. A device that 84 /// matches this filter must satisfy the following: 85 /// 1. Radio transmission power level and received signal strength must be 86 /// available for the path loss calculation. 87 /// 2. The calculated path loss value must be less than, or equal to, 88 /// `max_path_loss`. 89 /// 90 /// @note This field is calculated using the RSSI and TX Power information 91 /// obtained from advertising and scan response data during a scan 92 /// procedure. It should NOT be confused with information for an active 93 /// connection obtained using the "Path Loss Reporting" feature. 94 std::optional<uint8_t> max_path_loss; 95 }; 96 97 /// Parameters used during a scan. 98 struct ScanOptions { 99 /// List of filters for use during a scan. A peripheral that satisfies any 100 /// of these filters will be reported. At least 1 filter must be specified. 101 /// While not recommended, clients that require that all peripherals be 102 /// reported can specify an empty filter. 103 Vector<ScanFilter> filters; 104 105 /// The time interval between scans. 106 /// - Time = N * 0.625ms 107 /// - Range: 0x0004 (2.5ms) - 10.24ms (0x4000) 108 /// - Default: 10ms 109 uint16_t interval = 0x0010; 110 111 /// The duration of the scan. The window must be less than or equal to the 112 /// interval. 113 /// - Time = N * 0.625ms 114 /// - Range: 0x0004 (2.5ms) - 10.24ms (0x4000) 115 /// - Default: 10ms 116 uint16_t window = 0x0010; 117 }; 118 119 /// Information obtained from advertising and scan response data broadcast by 120 /// a peer. 121 struct ScanData { 122 /// The radio transmit power level. 123 /// @note This field should NOT be confused with the "connection TX Power 124 /// Level" of a peer that is currently connected to the system obtained via 125 /// the "Transmit Power reporting" feature. 126 std::optional<uint8_t> tx_power; 127 128 /// The appearance of the device. 129 std::optional<Appearance> appearance; 130 131 Vector<Uuid> service_uuids; 132 133 Vector<ServiceData> service_data; 134 135 Vector<ManufacturerData> manufacturer_data; 136 137 /// String representing a URI to be advertised, as defined in IETF STD 66: 138 /// https://tools.ietf.org/html/std66. Each entry should be a UTF-8 string 139 /// including the scheme. For more information, see 140 /// https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml for 141 /// allowed schemes; 142 /// @note Bluetooth advertising compresses schemas over the air to save 143 /// space. See 144 /// https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping. 145 Vector<std::string_view> uris; 146 147 /// The time when this scan data was received. 148 chrono::SystemClock::time_point timestamp; 149 }; 150 151 struct ScanResult { 152 /// ScanResult is non-copyable because strings are only valid in the result 153 /// callback. 154 ScanResult(const ScanResult&) = delete; 155 ScanResult& operator=(const ScanResult&) = delete; 156 157 /// Uniquely identifies this peer on the current system. 158 PeerId peer_id; 159 160 /// Whether or not this peer is connectable. Non-connectable peers are 161 /// typically in the LE broadcaster role. 162 bool connectable; 163 164 /// The last observed signal strength of this peer. This field is only 165 /// present for a peer that is broadcasting. The RSSI can be stale if the 166 /// peer has not been advertising. 167 /// 168 /// @note This field should NOT be confused with the "connection RSSI" of a 169 /// peer that is currently connected to the system. 170 std::optional<uint8_t> rssi; 171 172 /// Information from advertising and scan response data broadcast by this 173 /// peer. This contains the advertising data last received from the peer. 174 ScanData scan_data; 175 176 /// The name of this peer. The name is often obtained during a scan 177 /// procedure and can get updated during the name discovery procedure 178 /// following a connection. 179 /// 180 /// This field is present if the name is known. 181 std::optional<std::string_view> name; 182 183 /// Timestamp of when the information in this `ScanResult` was last updated. 184 chrono::SystemClock::time_point last_updated; 185 }; 186 187 /// Possible errors returned by `Connect`. 188 enum class ConnectError : uint8_t { 189 /// The peer ID is unknown. 190 kUnknownPeer, 191 192 /// The `ConnectionOptions` were invalid. 193 kInvalidOptions, 194 195 /// A connection to the peer already exists. 196 kAlreadyExists, 197 198 /// A connection could not be established. 199 kCouldNotBeEstablished, 200 }; 201 202 enum class StartScanError : uint8_t { 203 /// A scan is already in progress. Only 1 scan may be active at a time. 204 kScanInProgress, 205 /// Some of the scan options are invalid. 206 kInvalidParameters, 207 /// An internal error occurred and a scan could not be started. 208 kInternal, 209 }; 210 211 /// The Result type returned by Connect() via the passed callback. 212 using ConnectResult = Result<ConnectError, Connection::Ptr>; 213 214 virtual ~Central() = default; 215 216 /// Connect to the peer with the given identifier. 217 /// 218 /// The requested `Connection` represents the client's interest in the LE 219 /// connection to the peer. Destroying the `Connection` will disconnect from 220 /// the peer. Only 1 connection per peer may exist at a time. 221 /// 222 /// The `Connection` will be closed by the system if the connection to the 223 /// peer is lost or an error occurs, as indicated by `Connection.OnError`. 224 /// 225 /// @param peer_id Identifier of the peer to initiate a connection to. 226 /// @param options Options used to configure the connection. 227 /// @param callback Called when a connection is successfully established, or 228 /// an error occurs. 229 /// 230 /// Possible errors are documented in `ConnectError`. 231 virtual void Connect(PeerId peer_id, 232 ConnectionOptions options, 233 Function<void(ConnectResult)>&& callback) = 0; 234 235 /// Scans for nearby LE peripherals and broadcasters. The lifetime of the scan 236 /// session is tied to the returned `ScanHandle` object. Once a scan is 237 /// started, `scan_result_callback` will be called with scan results. Only 1 238 /// scan may be active at a time. It is OK to destroy the `ScanHandle::Ptr` 239 /// object in `scan_result_callback` to stop scanning (no more results will be 240 /// returned). 241 /// 242 /// @param options Options used to configure the scan session. 243 /// @param scan_result_callback If scanning starts successfully,called for LE 244 /// peers that satisfy the filters indicated in `options`. The initial calls 245 /// may report recently discovered peers. Subsequent calls will be made only 246 /// when peers have been scanned or updated since the last call. 247 /// @param scan_started_callback Called with a `ScanHandle` object if the scan 248 /// successfully starts, or a `ScanError` otherwise. 249 virtual void Scan(ScanOptions options, 250 Function<void(ScanResult)>&& scan_result_callback, 251 Function<void(Result<StartScanError, ScanHandle::Ptr>)>&& 252 scan_started_callback) = 0; 253 }; 254 255 } // namespace pw::bluetooth::low_energy 256