• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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