1 // Copyright 2024 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 <optional> 17 18 #include "pw_async2/once_sender.h" 19 #include "pw_bluetooth/internal/raii_ptr.h" 20 #include "pw_bluetooth/low_energy/connection2.h" 21 #include "pw_bluetooth/low_energy/phy.h" 22 #include "pw_bluetooth/types.h" 23 #include "pw_chrono/system_clock.h" 24 #include "pw_result/expected.h" 25 26 namespace pw::bluetooth::low_energy { 27 28 /// Represents the LE central role. Used to scan and connect to peripherals. 29 class Central2 { 30 public: 31 /// Filter parameters for use during a scan. A discovered peer only matches 32 /// the filter if it satisfies all of the present filter parameters. 33 struct ScanFilter { 34 /// Filter based on advertised service UUID. 35 std::optional<Uuid> service_uuid; 36 37 /// Filter based on service data containing the given UUID. 38 std::optional<Uuid> service_data_uuid; 39 40 /// Filter based on a manufacturer identifier present in the manufacturer 41 /// data. If this filter parameter is set, then the advertising payload must 42 /// contain manufacturer-specific data with the provided company identifier 43 /// to satisfy this filter. Manufacturer identifiers can be found at 44 /// [Assigned 45 /// Numbers](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/). 46 std::optional<uint16_t> manufacturer_id; 47 48 /// Filter based on whether or not a device is connectable. For example, a 49 /// client that is only interested in peripherals that it can connect to can 50 /// set this to true. Similarly a client can scan only for broadcasters by 51 /// setting this to false. 52 std::optional<bool> connectable; 53 54 /// Filter results based on a portion of the advertised device name. 55 /// Substring matches are allowed. 56 /// The name length must be at most `pw::bluetooth::kMaxDeviceNameLength`. 57 std::optional<std::string_view> name; 58 59 /// Filter results based on the path loss of the radio wave. A device that 60 /// matches this filter must satisfy the following: 61 /// 1. Radio transmission power level and received signal strength must be 62 /// available for the path loss calculation. 63 /// 2. The calculated path loss value must be less than, or equal to, 64 /// `max_path_loss`. 65 /// 66 /// @note This field is calculated using the RSSI and TX Power information 67 /// obtained from advertising and scan response data during a scan 68 /// procedure. It should NOT be confused with information for an active 69 /// connection obtained using the "Path Loss Reporting" feature. 70 std::optional<int8_t> max_path_loss; 71 72 /// Require that a peer solicits support for a service UUID. 73 std::optional<Uuid> solicitation_uuid; 74 }; 75 76 enum class ScanType : uint8_t { 77 kPassive, 78 /// Send scanning PDUs with the public address. 79 kActiveUsePublicAddress, 80 /// Send scanning PDUs with the random address. 81 kActiveUseRandomAddress, 82 /// Send scanning PDUs with a generated Resolvable Private Address. 83 kActiveUseResolvablePrivateAddress, 84 }; 85 86 /// Parameters used during a scan. 87 struct ScanOptions { 88 /// List of filters for use during a scan. A peripheral that satisfies any 89 /// of these filters will be reported. At least 1 filter must be specified. 90 /// While not recommended, clients that require that all peripherals be 91 /// reported can specify an empty filter. 92 /// The span memory must only be valid until the call to Scan() ends. 93 pw::span<const ScanFilter> filters; 94 95 /// The time interval between scans. 96 /// - Time = N * 0.625ms 97 /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000) 98 uint16_t interval; 99 100 /// The duration of the scan. The window must be less than or equal to the 101 /// interval. 102 /// - Time = N * 0.625ms 103 /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000) 104 uint16_t window; 105 106 /// Specifies whether to send scan requests, and if so, what type of address 107 /// to use in scan requests. 108 ScanType scan_type; 109 110 /// A bitmask of the PHYs to scan with. Only the 1Megabit and LeCoded PHYs 111 /// are supported. 112 Phy phys = Phy::k1Megabit; 113 }; 114 115 struct ScanResult { 116 /// Uniquely identifies this peer on the current system. 117 PeerId peer_id; 118 119 /// Whether or not this peer is connectable. Non-connectable peers are 120 /// typically in the LE broadcaster role. 121 bool connectable; 122 123 /// The last observed signal strength of this peer. This field is only 124 /// present for a peer that is broadcasting. The RSSI can be stale if the 125 /// peer has not been advertising. 126 /// 127 /// @note This field should NOT be confused with the "connection RSSI" of a 128 /// peer that is currently connected to the system. 129 std::optional<uint8_t> rssi; 130 131 /// This contains the advertising data last received from the peer. 132 pw::multibuf::MultiBuf data; 133 134 /// The name of this peer. The name is often obtained during a scan 135 /// procedure and can get updated during the name discovery procedure 136 /// following a connection. 137 /// 138 /// This field is present if the name is known. 139 std::optional<InlineString<22>> name; 140 141 /// Timestamp of when the information in this `ScanResult` was last updated. 142 chrono::SystemClock::time_point last_updated; 143 }; 144 145 /// Represents an ongoing LE scan. 146 class ScanHandle { 147 public: 148 /// Stops the scan. 149 virtual ~ScanHandle() = default; 150 151 /// Returns the next `ScanResult` if available. Otherwise, invokes 152 /// `cx.waker()` when a `ScanResult` is available. Only one waker is 153 /// supported at a time. 154 /// @return @rst 155 /// 156 /// .. pw-status-codes:: 157 /// 158 /// OK: ScanResult was returned. 159 /// 160 /// CANCELLED: An internal error occurred and the scan was cancelled. 161 /// 162 /// @endrst 163 virtual async2::Poll<pw::Result<ScanResult>> PendResult( 164 async2::Context& cx) = 0; 165 166 private: 167 /// Stop the current scan. This method is called by the ~ScanHandle::Ptr() 168 /// when it goes out of scope, the API client should never call this method. 169 virtual void Release() = 0; 170 171 public: 172 /// Movable ScanHandle smart pointer. The controller will continue scanning 173 /// until the ScanHandle::Ptr is destroyed. 174 using Ptr = internal::RaiiPtr<ScanHandle, &ScanHandle::Release>; 175 }; 176 177 /// Possible errors returned by `Connect`. 178 enum class ConnectError : uint8_t { 179 /// The peer ID is unknown. 180 kUnknownPeer, 181 182 /// The `ConnectionOptions` were invalid. 183 kInvalidOptions, 184 185 /// A connection to the peer already exists. 186 kAlreadyExists, 187 188 /// The connection procedure failed at the link layer or timed out 189 /// immediately after being established. A "could not be established" error 190 /// was reported by the controller. This may be due to interference. 191 kCouldNotBeEstablished, 192 }; 193 194 enum class StartScanError : uint8_t { 195 /// A scan is already in progress. Only 1 scan may be active at a time. 196 kScanInProgress, 197 /// Some of the scan options are invalid. 198 kInvalidParameters, 199 /// An internal error occurred and a scan could not be started. 200 kInternal, 201 }; 202 203 /// The result type returned by Connect(). 204 using ConnectResult = pw::expected<Connection2::Ptr, ConnectError>; 205 206 /// The result type returned by Scan(). 207 using ScanStartResult = pw::expected<ScanHandle::Ptr, StartScanError>; 208 209 virtual ~Central2() = default; 210 211 /// Connect to the peer with the given identifier. 212 /// 213 /// The returned `Connection2` represents the client's interest in the LE 214 /// connection to the peer. Destroying all `Connection2` instances for a peer 215 /// will disconnect from the peer. 216 /// 217 /// The `Connection` will be closed by the system if the connection to the 218 /// peer is lost or an error occurs, as indicated by `Connection.OnError`. 219 /// 220 /// @param peer_id Identifier of the peer to initiate a connection to. 221 /// @param options Options used to configure the connection. 222 /// @return Returns a result when a connection is successfully established, or 223 /// an error occurs. 224 /// 225 /// Possible errors are documented in `ConnectError`. 226 virtual async2::OnceReceiver<ConnectResult> Connect( 227 PeerId peer_id, Connection2::ConnectionOptions options) = 0; 228 229 /// Scans for nearby LE peripherals and broadcasters. The lifetime of the scan 230 /// session is tied to the returned `ScanHandle` object in `ScanStartResult`. 231 /// Once a scan is started, `ScanHandle::PendResult` can be called to get scan 232 /// results. Only 1 scan may be active at a time. 233 /// 234 /// @param options Options used to configure the scan session. These options 235 /// are *suggestions* only, and the implementation may use different 236 /// parameters to meet power or radio requirements. 237 /// @return Returns a `ScanHandle` object if the scan successfully 238 /// starts, or a `ScanError` otherwise. 239 /// `ScanHandle::PendResult` can be called to get `ScanResult`s for LE 240 /// peers that satisfy the filters indicated in `options`. The initial results 241 /// may report recently discovered peers. Subsequent results will be reported 242 /// only when peers have been scanned or updated since the last call. 243 virtual async2::OnceReceiver<ScanStartResult> Scan( 244 const ScanOptions& options) = 0; 245 }; 246 247 } // namespace pw::bluetooth::low_energy 248