forked from h2zero/esp-nimble-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNimBLEHIDDevice.cpp
More file actions
343 lines (302 loc) · 13.6 KB
/
NimBLEHIDDevice.cpp
File metadata and controls
343 lines (302 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "NimBLEHIDDevice.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLE2904.h"
static constexpr uint16_t deviceInfoSvcUuid = 0x180a;
static constexpr uint16_t hidSvcUuid = 0x1812;
static constexpr uint16_t batterySvcUuid = 0x180f;
static constexpr uint16_t pnpCharUuid = 0x2a50;
static constexpr uint16_t hidInfoCharUuid = 0x2a4a;
static constexpr uint16_t reportMapCharUuid = 0x2a4b;
static constexpr uint16_t hidControlCharUuid = 0x2a4c;
static constexpr uint16_t inputReportChrUuid = 0x2a4d;
static constexpr uint16_t protocolModeCharUuid = 0x2a4e;
static constexpr uint16_t batteryLevelCharUuid = 0x2a19;
static constexpr uint16_t batteryLevelDscUuid = 0x2904;
static constexpr uint16_t featureReportDscUuid = 0x2908;
static constexpr uint16_t m_manufacturerChrUuid = 0x2a29;
static constexpr uint16_t bootInputChrUuid = 0x2a22;
static constexpr uint16_t bootOutputChrUuid = 0x2a32;
/**
* @brief Construct a default NimBLEHIDDevice object.
* @param [in] server A pointer to the server instance this HID Device will use.
*/
NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
// Here we create mandatory services described in bluetooth specification
m_deviceInfoSvc = server->createService(deviceInfoSvcUuid);
m_hidSvc = server->createService(hidSvcUuid);
m_batterySvc = server->createService(batterySvcUuid);
// Mandatory characteristic for device info service
m_pnpChr = m_deviceInfoSvc->createCharacteristic(pnpCharUuid, NIMBLE_PROPERTY::READ);
// Mandatory characteristics for HID service
m_hidInfoChr = m_hidSvc->createCharacteristic(hidInfoCharUuid, NIMBLE_PROPERTY::READ);
m_reportMapChr = m_hidSvc->createCharacteristic(reportMapCharUuid, NIMBLE_PROPERTY::READ);
m_hidControlChr = m_hidSvc->createCharacteristic(hidControlCharUuid, NIMBLE_PROPERTY::WRITE_NR);
m_protocolModeChr =
m_hidSvc->createCharacteristic(protocolModeCharUuid, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
// Mandatory battery level characteristic with notification and presence descriptor
m_batteryLevelChr =
m_batterySvc->createCharacteristic(batteryLevelCharUuid, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
NimBLE2904* batteryLevelDescriptor = m_batteryLevelChr->create2904();
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setUnit(0x27ad); // percentage
// This value is setup here because its default value in most usage cases, it's very rare to use boot mode
m_protocolModeChr->setValue(static_cast<uint8_t>(0x01));
} // NimBLEHIDDevice
/**
* @brief Set the report map data formatting information.
* @param [in] map A pointer to an array with the values to set.
* @param [in] size The number of values in the array.
*/
void NimBLEHIDDevice::setReportMap(uint8_t* map, uint16_t size) {
m_reportMapChr->setValue(map, size);
} // setReportMap
/**
* @brief Start the HID device services.
* This function called when all the services have been created.
*/
void NimBLEHIDDevice::startServices() {
m_deviceInfoSvc->start();
m_hidSvc->start();
m_batterySvc->start();
} // startServices
/**
* @brief Get the manufacturer characteristic (this characteristic is optional).
* @details The characteristic will be created if not already existing.
* @returns True if the name was set and/or the characteristic created.
*/
bool NimBLEHIDDevice::setManufacturer(const std::string& name) {
if (m_manufacturerChr == nullptr) {
m_manufacturerChr = m_deviceInfoSvc->createCharacteristic(m_manufacturerChrUuid, NIMBLE_PROPERTY::READ);
}
if (m_manufacturerChr) {
m_manufacturerChr->setValue(name);
return true;
}
return false;
} // setManufacturer
/**
* @brief Sets the Plug n Play characteristic value.
* @param [in] sig The vendor ID source number.
* @param [in] vid The vendor ID number.
* @param [in] pid The product ID number.
* @param [in] version The produce version number.
*/
void NimBLEHIDDevice::setPnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
uint8_t pnp[] = {sig,
static_cast<uint8_t>(vid & 0xFF),
static_cast<uint8_t>((vid >> 8) & 0xFF),
static_cast<uint8_t>(pid & 0xFF),
static_cast<uint8_t>((pid >> 8) & 0xFF),
static_cast<uint8_t>(version & 0xFF),
static_cast<uint8_t>((version >> 8) & 0xFF)};
m_pnpChr->setValue(pnp, sizeof(pnp));
} // setPnp
/**
* @brief Sets the HID Information characteristic value.
* @param [in] country The country code for the device.
* @param [in] flags The HID Class Specification release number to use.
*/
void NimBLEHIDDevice::setHidInfo(uint8_t country, uint8_t flags) {
uint8_t info[] = {0x11, 0x1, country, flags};
m_hidInfoChr->setValue(info, sizeof(info));
} // setHidInfo
/**
* @brief Set the battery level characteristic value.
* @param [in] level The battery level value.
* @param [in] notify If true sends a notification to the peer device, otherwise not. default = false
*/
void NimBLEHIDDevice::setBatteryLevel(uint8_t level, bool notify) {
m_batteryLevelChr->setValue(&level, 1);
if (notify) {
m_batteryLevelChr->notify();
}
} // setBatteryLevel
/**
* @brief Locate the characteristic for a report ID and a report type.
*
* @param [in] reportId Report identifier to locate.
* @param [in] reportType Type of report (input/output/feature).
* @return NimBLECharacteristic* The characteristic.
* @return nullptr If the characteristic does not exist.
*/
NimBLECharacteristic* NimBLEHIDDevice::locateReportCharacteristicByIdAndType(uint8_t reportId, uint8_t reportType) {
NimBLECharacteristic* candidate = m_hidSvc->getCharacteristic(inputReportChrUuid, 0);
for (uint16_t i = 1; (candidate != nullptr) && (i != 0); i++) {
NimBLEDescriptor* dsc = candidate->getDescriptorByUUID(featureReportDscUuid);
NimBLEAttValue desc1_val_att = dsc->getValue();
const uint8_t* desc1_val = desc1_val_att.data();
if ((desc1_val[0] == reportId) && (desc1_val[1] == reportType)) return candidate;
candidate = m_hidSvc->getCharacteristic(inputReportChrUuid, i);
}
return nullptr;
}
/**
* @brief Get the input report characteristic.
* @param [in] reportId Input report ID, the same as in report map for input object related to the characteristic.
* @return NimBLECharacteristic* A pointer to the input report characteristic.
* Store this value to avoid computational overhead.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
NimBLECharacteristic* inputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x01);
if (inputReportChr == nullptr) {
inputReportChr =
m_hidSvc->createCharacteristic(inputReportChrUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor* inputReportDsc =
inputReportChr->createDescriptor(featureReportDscUuid, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = {reportId, 0x01};
inputReportDsc->setValue(desc1_val, 2);
}
return inputReportChr;
} // getInputReport
/**
* @brief Get the output report characteristic.
* @param [in] reportId Output report ID, the same as in report map for output object related to the characteristic.
* @return NimBLECharacteristic* A pointer to the output report characteristic.
* Store this value to avoid computational overhead.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
NimBLECharacteristic* outputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x02);
if (outputReportChr == nullptr) {
outputReportChr =
m_hidSvc->createCharacteristic(inputReportChrUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR |
NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
NimBLEDescriptor* outputReportDsc = outputReportChr->createDescriptor(
featureReportDscUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = {reportId, 0x02};
outputReportDsc->setValue(desc1_val, 2);
}
return outputReportChr;
} // getOutputReport
/**
* @brief Get the feature report characteristic.
* @param [in] reportId Feature report ID, the same as in report map for feature object related to the characteristic.
* @return NimBLECharacteristic* A pointer to feature report characteristic.
* Store this value to avoid computational overhead.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getFeatureReport(uint8_t reportId) {
NimBLECharacteristic* featureReportChr = locateReportCharacteristicByIdAndType(reportId, 0x03);
if (featureReportChr == nullptr) {
featureReportChr = m_hidSvc->createCharacteristic(
inputReportChrUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
NimBLEDescriptor* featureReportDsc = featureReportChr->createDescriptor(
featureReportDscUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = {reportId, 0x03};
featureReportDsc->setValue(desc1_val, 2);
}
return featureReportChr;
} // getFeatureReport
/**
* @brief Get a keyboard boot input report characteristic.
* @returns A pointer to the boot input report characteristic, or nullptr on error.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getBootInput() {
NimBLECharacteristic* bootInputChr = m_hidSvc->getCharacteristic(bootInputChrUuid);
if (bootInputChr) {
return bootInputChr;
}
return m_hidSvc->createCharacteristic(bootInputChrUuid, NIMBLE_PROPERTY::NOTIFY);
} // getBootInput
/**
* @brief Create a keyboard boot output report characteristic
* @returns A pointer to the boot output report characteristic, or nullptr on error.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getBootOutput() {
NimBLECharacteristic* bootOutputChr = m_hidSvc->getCharacteristic(bootOutputChrUuid);
if (bootOutputChr) {
return bootOutputChr;
}
return m_hidSvc->createCharacteristic(bootOutputChrUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
} // getBootOutput
/**
* @brief Get the HID control point characteristic.
* @returns A pointer to the HID control point characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::getHidControl() {
return m_hidControlChr;
} // getHidControl
/**
* @brief Get the HID protocol mode characteristic.
* @returns a pointer to the protocol mode characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::getProtocolMode() {
return m_protocolModeChr;
} // getProtocolMode
/**
* @brief Get the battery level characteristic
* @returns A pointer to the battery level characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::getBatteryLevel() {
return m_batteryLevelChr;
} // getBatteryLevel
/**
* @brief Get the report map characteristic.
* @returns A pointer to the report map characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::getReportMap() {
return m_reportMapChr;
} // getReportMap
/**
* @brief Get the PnP characteristic.
* @returns A pointer to the PnP characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::getPnp() {
return m_pnpChr;
} // getPnp
/**
* @brief Get the HID information characteristic.
* @returns A pointer to the HID information characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::getHidInfo() {
return m_hidInfoChr;
} // hidInfo
/**
* @brief Get the manufacturer characteristic.
* @returns A pointer to the manufacturer characteristic.
*/
NimBLEService* NimBLEHIDDevice::getDeviceInfoService() {
return m_deviceInfoSvc;
} // getDeviceInfoService
/**
* @brief Get the HID service.
* @returns A pointer to the HID service.
*/
NimBLEService* NimBLEHIDDevice::getHidService() {
return m_hidSvc;
} // getHidService
/**
* @brief Get the battery service.
* @returns A pointer to the battery service.
*/
NimBLEService* NimBLEHIDDevice::getBatteryService() {
return m_batterySvc;
} // getBatteryService
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)