/
SerialBTE.m
256 lines (229 loc) · 8.91 KB
/
SerialBTE.m
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
/* Copyright (C) 2023 Salvatore Sanfilippo <antirez@gmail.com>
* All Rights Reserved
*
* This code is released under the BSD 2 clause license.
* See the LICENSE file for more information. */
#include <stdio.h>
#include <unistd.h>
#import "SerialBTE.h"
#include "linenoise.h"
struct linenoiseState LineNoiseState;
char LineNoiseBuffer[1024];
@implementation SerialBTE
@synthesize discoveredDevices;
- (instancetype)init
{
return [self initWithNamePattern: nil];
}
- (instancetype)initWithNamePattern:(NSString *) pattern
{
linenoiseEditStart(&LineNoiseState,-1,-1,LineNoiseBuffer,sizeof(LineNoiseBuffer),"serial> ");
self = [super init];
if (self) {
self.discoveredDevices = [NSMutableArray array];
serviceUuid = [CBUUID UUIDWithString: @"6E400001-B5A3-F393-E0A9-E50E24DCCA9E"];
readChar = nil;
writeChar = nil;
peripheral = nil;
namepat = pattern;
btequeue = dispatch_get_main_queue();
_manager = [[CBCentralManager alloc] initWithDelegate: self
queue: btequeue];
}
/* Register a callback() for when there is data in the standard input,
* that is, the user typed something. */
int fileDescriptor = fileno(stdin);
dispatch_source_t stdinSource =
dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
fileDescriptor, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(stdinSource, ^{
char *line = linenoiseEditFeed(&LineNoiseState);
if (line == linenoiseEditMore) return;
linenoiseEditStop(&LineNoiseState);
if (line == NULL) exit(0);
linenoiseHistoryAdd(line); /* Add to the history. */
/* Write what the user typed to the device BLE serial. */
if (peripheral != nil && writeChar != nil) {
size_t l = strlen(line);
if (l > 1 && line[l-1] == '\n') line[l-1] = 0;
NSString *stringValue = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
NSData *dataValue = [stringValue dataUsingEncoding:NSUTF8StringEncoding];
[peripheral writeValue:dataValue forCharacteristic:writeChar type:CBCharacteristicWriteWithResponse];
}
/* Reset line editing state. */
linenoiseFree(line);
linenoiseEditStart(&LineNoiseState,-1,-1,LineNoiseBuffer,sizeof(LineNoiseBuffer),"serial> ");
});
dispatch_resume(stdinSource);
return self;
}
- (void)dealloc
{
[_manager stopScan];
[_manager dealloc];
[super dealloc];
}
/* This is called when our Central Manager is ready. So this is the
* entry point of the program in some way. Here we will start
* a BTE scanning or other operations. */
- (void)centralManagerDidUpdateState:(CBCentralManager *)manager
{
if ([manager state] == CBManagerStatePoweredOn) {
[self startScan];
}
}
/* Called each time the Central Manager discovers a peripheral
* while scanning. */
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)aPeripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
NSMutableArray *peripherals = [self mutableArrayValueForKey:@"discoveredDevices"];
const char *deviceName = [[aPeripheral name] cStringUsingEncoding:NSASCIIStringEncoding];
NSString *localNameValue = [advertisementData valueForKey:@"kCBAdvDataLocalName"];
const char *localName = localNameValue != nil ? [localNameValue cStringUsingEncoding:NSASCIIStringEncoding] : NULL;
if (deviceName) {
linenoiseHide(&LineNoiseState);
printf("Found: %s (%s): ", deviceName, localName ? localName : "?");
linenoiseShow(&LineNoiseState);
if (namepat != nil) {
const char *pat = [namepat cStringUsingEncoding:NSASCIIStringEncoding];
if (strcasestr(deviceName,pat) == NULL &&
(localName == NULL || strcasestr(localName,pat) == NULL))
{
linenoiseHide(&LineNoiseState);
printf("Discarding (name mismatch)\n");
linenoiseShow(&LineNoiseState);
return;
}
}
linenoiseHide(&LineNoiseState);
printf("Connecting...\n");
linenoiseShow(&LineNoiseState);
} else {
return;
}
/* Conencting will fail if we don't create a durable
* reference to the peripheral, so we add it into the array. */
[peripherals addObject:aPeripheral];
[self connectToPeripheral: aPeripheral];
}
/* Called as a result of connectToPeripheral, once the connection
* is finalized. */
- (void) centralManager: (CBCentralManager *)central
didConnectPeripheral: (CBPeripheral *)aPeripheral
{
linenoiseHide(&LineNoiseState);
printf("Connected.\n");
linenoiseShow(&LineNoiseState);
[aPeripheral setDelegate:self];
[aPeripheral discoverServices:nil];
}
/* Called if on disconnection from the currently connected device. */
- (void) centralManager: (CBCentralManager *)central
didDisconnectPeripheral: (CBPeripheral *)aPeripheral
error: (NSError *)error
{
fprintf(stderr,"\r\nDevice disconnected. Exiting...\r\n");
exit(1);
}
/* Called on connection error. */
- (void) centralManager: (CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)aPeripheral error:(NSError *)error
{
linenoiseHide(&LineNoiseState);
linenoiseHide(&LineNoiseState);
NSLog(@"Fail to connect to peripheral: %@ with error = %@", aPeripheral, [error localizedDescription]);
linenoiseShow(&LineNoiseState);
linenoiseShow(&LineNoiseState);
}
/* Start scanning. */
- (void) startScan
{
linenoiseHide(&LineNoiseState);
printf("Start scanning\n");
linenoiseShow(&LineNoiseState);
if (!serviceUuid)
{
[_manager scanForPeripheralsWithServices: nil options: nil];
}
else
{
[_manager scanForPeripheralsWithServices: [NSArray arrayWithObject: serviceUuid] options: nil];
}
}
/* Connect to the specified device. */
- (void) connectToPeripheral: (CBPeripheral *)aPeripheral
{
// printf("Stop scannign and connecting\n");
[_manager stopScan];
peripheral = aPeripheral;
NSDictionary *connectOptions = @{
CBConnectPeripheralOptionNotifyOnConnectionKey: @YES,
CBConnectPeripheralOptionNotifyOnDisconnectionKey: @YES,
CBConnectPeripheralOptionNotifyOnNotificationKey: @YES,
CBConnectPeripheralOptionStartDelayKey: @0
};
[_manager connectPeripheral:peripheral options:connectOptions];
}
/* Called on completion of discoverServices call, will report the
* discovered services provided by the device. */
- (void) peripheral: (CBPeripheral *)aPeripheral
didDiscoverServices:(NSError *)error
{
for (CBService *aService in aPeripheral.services)
{
linenoiseHide(&LineNoiseState);
NSLog(@"Service: %@", aService.UUID);
linenoiseShow(&LineNoiseState);
if ([aService.UUID isEqual:serviceUuid]) {
[aPeripheral discoverCharacteristics:nil forService:aService];
}
}
}
/* Called upon completion of discoverCharacteristics, with an array of
* characteristics provided by a given service of the device. */
- (void) peripheral: (CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
for (CBCharacteristic *aChar in service.characteristics)
{
// NSLog(@"Service: %@ with Char: %@", [aChar service].UUID, aChar.UUID);
if (aChar.properties & CBCharacteristicPropertyNotify)
{
linenoiseHide(&LineNoiseState);
printf("Notify characteristic found.\r\n");
[aPeripheral setNotifyValue:YES forCharacteristic:aChar];
[aPeripheral readValueForCharacteristic:aChar];
readChar = aChar;
[readChar retain];
NSLog(@"Notify Char: %@ about service %@", readChar.UUID, service.UUID);
linenoiseShow(&LineNoiseState);
} else if (aChar.properties & CBCharacteristicPropertyWrite) {
writeChar = aChar;
[writeChar retain];
linenoiseHide(&LineNoiseState);
printf("Write characteristic found.\r\n");
NSLog(@"Write Char: %@ about service %@", writeChar.UUID, service.UUID);
linenoiseShow(&LineNoiseState);
}
}
}
/* Callback of reads, after readValueForCharacteristic. */
- (void) peripheral: (CBPeripheral *)aPeripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSData *newval = characteristic.value;
if (newval) {
size_t l = [newval length];
char *p = (char*)newval.bytes;
while (l && p[l-1] == '\n') l--;
linenoiseHide(&LineNoiseState);
printf("%.*s\r\n", (int)l, p);
linenoiseShow(&LineNoiseState);
}
}
- (void)peripheral: (CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices
{
printf("[didModifyServices] called\r\n");
exit(1);
}
@end