esp-nimble-cpp 2.5.0
Loading...
Searching...
No Matches
NimBLEStream.h
1/*
2 * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
3 * esp-nimble-cpp, NimBLE-Arduino contributors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#ifndef NIMBLE_CPP_STREAM_H
19#define NIMBLE_CPP_STREAM_H
20
21#include "syscfg/syscfg.h"
22#if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
23
24# ifdef USING_NIMBLE_ARDUINO_HEADERS
25# include "nimble/nimble/include/nimble/nimble_npl.h"
26# else
27# include "nimble/nimble_npl.h"
28# endif
29
30# include <functional>
31# include <type_traits>
32# include <cstdarg>
33
34# ifndef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
35# define NIMBLE_CPP_ARDUINO_STRING_AVAILABLE (__has_include(<Arduino.h>))
36# endif
37
38# if NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
39# include <Stream.h>
40# else
41
42// Minimal Stream/Print stubs when Arduino not available
43class Print {
44 public:
45 virtual ~Print() {}
46 virtual size_t write(uint8_t) = 0;
47 virtual size_t write(const uint8_t* buffer, size_t size) = 0;
48 size_t print(const char* s);
49 size_t println(const char* s);
50 size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3)));
51};
52
53class Stream : public Print {
54 public:
55 virtual int available() = 0;
56 virtual int read() = 0;
57 virtual int peek() = 0;
58 virtual void flush() {}
59 void setTimeout(unsigned long timeout) { m_timeout = timeout; }
60 unsigned long getTimeout() const { return m_timeout; }
61
62 protected:
63 unsigned long m_timeout{0};
64};
65# endif
66
67class NimBLEStream : public Stream {
68 public:
69 enum RxOverflowAction {
70 DROP_OLDER_DATA, // Drop older buffered data to make room for new data
71 DROP_NEW_DATA // Drop new incoming data when buffer is full
72 };
73
74 using RxOverflowCallback = std::function<RxOverflowAction(const uint8_t* data, size_t len, void* userArg)>;
75
76 NimBLEStream() = default;
77 virtual ~NimBLEStream() { end(); }
78
79 // Print/Stream TX methods
80 virtual size_t write(const uint8_t* data, size_t len) override;
81 virtual size_t write(uint8_t data) override { return write(&data, 1); }
82
83 // Template for other integral types (char, int, long, etc.)
84 template <typename T>
85 typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, uint8_t>::value, size_t>::type write(T data) {
86 return write(static_cast<uint8_t>(data));
87 }
88
89 size_t availableForWrite() const;
90
91 // Read up to len bytes into buffer (non-blocking)
92 size_t read(uint8_t* buffer, size_t len);
93
94 // Stream RX methods
95 virtual int available() override;
96 virtual int read() override;
97 virtual int peek() override;
98 virtual bool ready() const = 0;
99
105 void setRxOverflowCallback(RxOverflowCallback cb, void* userArg = nullptr) {
106 m_rxOverflowCallback = cb;
107 m_rxOverflowUserArg = userArg;
108 }
109
110 operator bool() const { return ready(); }
111
112 using Print::write;
113
114 struct ByteRingBuffer;
115
116 protected:
117 bool begin();
118 void drainTx();
119 size_t pushRx(const uint8_t* data, size_t len);
120 virtual void end();
121 virtual bool send() = 0;
122 static void txDrainEventCb(struct ble_npl_event* ev);
123 static void txDrainCalloutCb(struct ble_npl_event* ev);
124
125 ByteRingBuffer* m_txBuf{nullptr};
126 ByteRingBuffer* m_rxBuf{nullptr};
127 uint8_t m_txChunkBuf[MYNEWT_VAL(BLE_ATT_PREFERRED_MTU)];
128 uint32_t m_txBufSize{1024};
129 uint32_t m_rxBufSize{1024};
130 ble_npl_event m_txDrainEvent{};
131 ble_npl_callout m_txDrainCallout{};
132 RxOverflowCallback m_rxOverflowCallback{nullptr};
133 void* m_rxOverflowUserArg{nullptr};
134 bool m_coInitialized{false};
135 bool m_eventInitialized{false};
136};
137
138# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
139# include "NimBLECharacteristic.h"
140
141class NimBLEStreamServer : public NimBLEStream {
142 public:
143 NimBLEStreamServer() : m_charCallbacks(this) {}
144 ~NimBLEStreamServer() override { end(); }
145
146 // non-copyable
147 NimBLEStreamServer(const NimBLEStreamServer&) = delete;
148 NimBLEStreamServer& operator=(const NimBLEStreamServer&) = delete;
149
150 bool begin(NimBLECharacteristic* chr, uint32_t txBufSize = 1024, uint32_t rxBufSize = 1024);
151
152 // Convenience overload to create service/characteristic internally; service will be deleted on end()
153 bool begin(const NimBLEUUID& svcUuid,
154 const NimBLEUUID& chrUuid,
155 uint32_t txBufSize = 1024,
156 uint32_t rxBufSize = 1024,
157 bool secure = false);
158
159 void end() override;
160 size_t write(const uint8_t* data, size_t len) override;
161 uint16_t getPeerHandle() const { return m_charCallbacks.m_peerHandle; }
162 void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { m_charCallbacks.m_userCallbacks = pCallbacks; }
163 bool ready() const override;
164 virtual void flush() override;
165
166 using NimBLEStream::write; // Inherit template write overloads
167
168 protected:
169 bool send() override;
170
171 struct ChrCallbacks : public NimBLECharacteristicCallbacks {
172 ChrCallbacks(NimBLEStreamServer* parent)
173 : m_parent(parent), m_userCallbacks(nullptr), m_peerHandle(BLE_HS_CONN_HANDLE_NONE) {}
174 void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override;
175 void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override;
176 void onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code) override;
177 // override this to avoid recursion when debug logs are enabled
178 void onStatus(NimBLECharacteristic* pCharacteristic, int code) override {
179 if (m_userCallbacks != nullptr) {
180 m_userCallbacks->onStatus(pCharacteristic, code);
181 }
182 }
183
184 NimBLEStreamServer* m_parent;
185 NimBLECharacteristicCallbacks* m_userCallbacks;
186 uint16_t m_peerHandle;
187 } m_charCallbacks;
188
189 NimBLECharacteristic* m_pChr{nullptr};
190 int m_rc{0};
191 // Whether to delete the BLE service when end() is called; set to false if service is managed externally
192 bool m_deleteSvcOnEnd{false};
193};
194# endif // BLE_ROLE_PERIPHERAL
195
196# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
197# include "NimBLERemoteCharacteristic.h"
198
199class NimBLEStreamClient : public NimBLEStream {
200 public:
201 NimBLEStreamClient() = default;
202 ~NimBLEStreamClient() override { end(); }
203
204 // non-copyable
205 NimBLEStreamClient(const NimBLEStreamClient&) = delete;
206 NimBLEStreamClient& operator=(const NimBLEStreamClient&) = delete;
207
208 // Attach a discovered remote characteristic; app owns discovery/connection.
209 // Set subscribeNotify=true to receive notifications into RX buffer.
210 bool begin(NimBLERemoteCharacteristic* pChr,
211 bool subscribeNotify = false,
212 uint32_t txBufSize = 1024,
213 uint32_t rxBufSize = 1024);
214 void end() override;
215 void setNotifyCallback(NimBLERemoteCharacteristic::notify_callback cb) { m_userNotifyCallback = cb; }
216 bool ready() const override;
217 virtual void flush() override;
218
219 using NimBLEStream::write; // Inherit template write overloads
220
221 protected:
222 bool send() override;
223 void notifyCallback(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t len, bool isNotify);
224
225 NimBLERemoteCharacteristic* m_pChr{nullptr};
226 NimBLERemoteCharacteristic::notify_callback m_userNotifyCallback{nullptr};
227};
228# endif // BLE_ROLE_CENTRAL
229
230#endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
231#endif // NIMBLE_CPP_STREAM_H
Connection information.
Definition NimBLEConnInfo.h:33
A model of a BLE UUID.
Definition NimBLEUUID.h:41