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 <memory> 17 #include <optional> 18 19 #include "pw_async2/once_sender.h" 20 #include "pw_bluetooth/internal/raii_ptr.h" 21 #include "pw_bluetooth/low_energy/advertising_data.h" 22 #include "pw_bluetooth/low_energy/connection2.h" 23 #include "pw_bluetooth/low_energy/phy.h" 24 #include "pw_bluetooth/types.h" 25 #include "pw_chrono/system_clock.h" 26 #include "pw_containers/vector.h" 27 #include "pw_function/function.h" 28 #include "pw_result/expected.h" 29 30 namespace pw::bluetooth::low_energy { 31 32 /// Represents the LE central role. Used to scan and connect to peripherals. 33 class Central2 { 34 public: 35 /// Filter parameters for use during a scan. A discovered peer only matches 36 /// the filter if it satisfies all of the present filter parameters. 37 struct ScanFilter { 38 /// Filter based on advertised service UUID. 39 std::optional<Uuid> service_uuid; 40 41 /// Filter based on service data containing the given UUID. 42 std::optional<Uuid> service_data_uuid; 43 44 /// Filter based on a manufacturer identifier present in the manufacturer 45 /// data. If this filter parameter is set, then the advertising payload must 46 /// contain manufacturer-specific data with the provided company identifier 47 /// to satisfy this filter. Manufacturer identifiers can be found at 48 /// [Assigned 49 /// Numbers](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/). 50 std::optional<uint16_t> manufacturer_id; 51 52 /// Filter based on whether or not a device is connectable. For example, a 53 /// client that is only interested in peripherals that it can connect to can 54 /// set this to true. Similarly a client can scan only for broadcasters by 55 /// setting this to false. 56 std::optional<bool> connectable; 57 58 /// Filter results based on a portion of the advertised device name. 59 /// Substring matches are allowed. 60 /// The name length must be at most `pw::bluetooth::kMaxDeviceNameLength`. 61 std::optional<std::string_view> name; 62 63 /// Filter results based on the path loss of the radio wave. A device that 64 /// matches this filter must satisfy the following: 65 /// 1. Radio transmission power level and received signal strength must be 66 /// available for the path loss calculation. 67 /// 2. The calculated path loss value must be less than, or equal to, 68 /// `max_path_loss`. 69 /// 70 /// @note This field is calculated using the RSSI and TX Power information 71 /// obtained from advertising and scan response data during a scan 72 /// procedure. It should NOT be confused with information for an active 73 /// connection obtained using the "Path Loss Reporting" feature. 74 std::optional<uint8_t> max_path_loss; 75 }; 76 77 enum class ScanType : uint8_t { 78 kPassive, 79 /// Send scanning PDUs with the public address. 80 kActiveUsePublicAddress, 81 /// Send scanning PDUs with the random address. 82 kActiveUseRandomAddress, 83 /// Send scanning PDUs with a generated Resolvable Private Address. 84 kActiveUseResolvablePrivateAddress, 85 }; 86 87 /// Parameters used during a scan. 88 struct ScanOptions { 89 /// List of filters for use during a scan. A peripheral that satisfies any 90 /// of these filters will be reported. At least 1 filter must be specified. 91 /// While not recommended, clients that require that all peripherals be 92 /// reported can specify an empty filter. 93 /// The span memory must only be valid until the call to Scan() ends. 94 pw::span<const ScanFilter> filters; 95 96 /// The time interval between scans. 97 /// - Time = N * 0.625ms 98 /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000) 99 uint16_t interval; 100 101 /// The duration of the scan. The window must be less than or equal to the 102 /// interval. 103 /// - Time = N * 0.625ms 104 /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000) 105 uint16_t window; 106 107 /// Specifies whether to send scan requests, and if so, what type of address 108 /// to use in scan requests. 109 ScanType scan_type; 110 111 /// A bitmask of the PHYs to scan with. Only the 1Megabit and LeCoded PHYs 112 /// are supported. 113 Phy phys = Phy::k1Megabit; 114 }; 115 116 struct ScanResult { 117 /// Uniquely identifies this peer on the current system. 118 PeerId peer_id; 119 120 /// Whether or not this peer is connectable. Non-connectable peers are 121 /// typically in the LE broadcaster role. 122 bool connectable; 123 124 /// The last observed signal strength of this peer. This field is only 125 /// present for a peer that is broadcasting. The RSSI can be stale if the 126 /// peer has not been advertising. 127 /// 128 /// @note This field should NOT be confused with the "connection RSSI" of a 129 /// peer that is currently connected to the system. 130 std::optional<uint8_t> rssi; 131 132 /// This contains the advertising data last received from the peer. 133 pw::multibuf::MultiBuf data; 134 135 /// The name of this peer. The name is often obtained during a scan 136 /// procedure and can get updated during the name discovery procedure 137 /// following a connection. 138 /// 139 /// This field is present if the name is known. 140 std::optional<InlineString<22>> name; 141 142 /// Timestamp of when the information in this `ScanResult` was last updated. 143 chrono::SystemClock::time_point last_updated; 144 }; 145 146 /// Represents an ongoing LE scan. 147 class ScanHandle { 148 public: 149 virtual ~ScanHandle() = 0; 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 StopScan() = 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::StopScan>; 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(ScanOptions options) = 0; 244 }; 245 246 } // namespace pw::bluetooth::low_energy 247