-
Notifications
You must be signed in to change notification settings - Fork 18
/
arduino-ble-ident-n-set.ino
489 lines (444 loc) · 13.5 KB
/
arduino-ble-ident-n-set.ino
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
// Arduino BLE module identification and setup sketch
// Copyright, Arik Yavilevich
#ifdef __AVR__ // supports modules connected over software serial
#include <SoftwareSerial.h>
#endif
/// Consts
// BLE module default pins
#ifdef ESP32
#define RX_PIN 16
#define TX_PIN 17
#define STATE_PIN 5
#else
#define RX_PIN 8
#define TX_PIN 9
#define STATE_PIN 7
#endif
#define STATE_PIN_MISSING -1
// misc
#define SERIAL_BAUD 115200
#define SERIAL_TIMEOUT 60000 // 1 min
#define BLE_BAUD 9600 // for AT mode and for data mode (CC41, HM-10 and MLT-BT05)
#define BLE_TIMEOUT 250 // 100ms was ok for CC41 and HM-10 but not for MLT_BT05's AT+HELP command
#define INITIAL_DELAY 200 // in ms
enum ModuleType { HM10, CC41, MLT_BT05, Unknown };
enum Operation {Quit, SetName, SetPass, SetStateBehavior, SetPower, SetBindingType, DisplayMainSettings, RestoreDefaults, Reboot, ReIdentifyDevice, DetermineConnectionState, SetRole, SendCommand};
enum ConnectionState {NoStatePin, Blinking, Connected, Disconnected};
// Support hardware serials and predefined serials
#ifdef ESP32
HardwareSerial Serial2(2);
#define SPECIAL_SERIAL Serial2
#endif
/// State
Stream * ble = NULL; // common interface of hardware/software serial
int rxPin, txPin, statePin;
ModuleType moduleType;
/// Special functions
// define here due to custom return type
ModuleType identifyDevice();
ConnectionState getConnectionState();
void doCommandAndEchoResult(const char * command, const __FlashStringHelper * meaning = NULL);
Operation getMenuSelection();
// Serial line end modes
// CR \r
// LF \n
// CR&LF \r\n
void setup()
{
Serial.begin(SERIAL_BAUD);
Serial.setTimeout(SERIAL_TIMEOUT);
delay(INITIAL_DELAY);
Serial.println(F("Arduino BLE module identification and setup sketch."));
Serial.println(F("Interact with this interface using serial in CR&LF mode."));
// Serial.println(F(""));
do
{
do
{
openBLE();
} while (determineInitialConnectionState() == false);
} while ((moduleType=identifyDevice()) == Unknown);
displayMainSettings();
do
{
Operation op = getMenuSelection();
switch (op)
{
case SetName:
setName();
break;
case SetPass:
setPass();
break;
case SetStateBehavior:
setStateBehavior();
break;
case SetPower:
setPower();
break;
case SetBindingType:
setBindingType();
break;
case DisplayMainSettings:
displayMainSettings();
break;
case RestoreDefaults:
restoreDefaults();
break;
case Reboot:
reboot();
break;
case ReIdentifyDevice:
moduleType = identifyDevice();
break;
case DetermineConnectionState:
printConnectionState();
break;
case SetRole:
setRole();
break;
case SendCommand:
sendCommand();
break;
default: // timeout and no option was selected
Serial.println(F("Quitting. Sketch ended."));
return;
}
} while (true);
}
/// BL functions
void openBLE()
{
rxPin=readInt(F("Enter the number of the RX pin on the Arduino, TX on the module"), RX_PIN);
txPin = readInt(F("Enter the number of the TX pin on the Arduino, RX on the module"), TX_PIN);
statePin = readInt(F("Enter the number of the State pin on the Arduino, State on the module (enter -1 if not present or not connected)"), STATE_PIN);
Serial.print(F("Opening serial connection to BLE module at pins: "));
Serial.print(rxPin);
Serial.print(F(", "));
Serial.print(txPin);
Serial.print(F(", "));
Serial.print(statePin);
Serial.println();
// open and create object
#ifndef SPECIAL_SERIAL
if (ble)
delete ble;
SoftwareSerial * ss = new SoftwareSerial(rxPin, txPin);
ss->begin(BLE_BAUD);
ble = ss;
#else
ble = &SPECIAL_SERIAL;
SPECIAL_SERIAL.begin(BLE_BAUD, SERIAL_8N1, rxPin, txPin);
#endif
ble->setTimeout(BLE_TIMEOUT);
if(statePin!=STATE_PIN_MISSING)
pinMode(statePin, INPUT);
}
ConnectionState getConnectionState()
{
if (statePin == STATE_PIN_MISSING)
return NoStatePin;
// read state
int state = digitalRead(statePin);
// check if state is "blinking"
unsigned long p1 = pulseIn(statePin, HIGH, 2000000);
unsigned long p2 = pulseIn(statePin, LOW, 2000000);
if (p1 == p2 && p1 == 0) // if no pulse detected
{
if (state == HIGH)
return Connected;
else
return Disconnected;
}
return Blinking;
}
// perform initial connection state detection including sending user messages/instructions
// returns true is can detect that the device is in data mode
// returns false if can't dtermine or if in command mode
bool determineInitialConnectionState()
{
if (statePin != STATE_PIN_MISSING)
Serial.println(F("Checking module state..."));
switch (getConnectionState())
{
case Connected:
Serial.println(F("The signal on the state pin is HIGH. This means the device is connected and is in data mode. Sketch can't proceed."));
return false;
case Disconnected:
Serial.println(F("The signal on the state pin is LOW. This means the device is not connected and is in command mode."));
break;
case Blinking:
Serial.println(F("The signal on the state pin is \"blinking\". This usually means the device is not connected and is in command mode."));
Serial.println(F("Consider reconfiguring the device to pass the state as-is. So it is simple to detect the state."));
break;
case NoStatePin:
Serial.println(F("For this sketch, make sure the module is not connected to another BLE device."));
Serial.println(F("This will make sure the device is in command mode."));
Serial.println(F("A led on the module should be off or blinking."));
break;
default:
Serial.println(F("Unknown connection state."));
break;
}
return true;
}
void printConnectionState()
{
Serial.println(F("Checking module state..."));
switch (getConnectionState())
{
case Connected:
Serial.println(F("The signal on the state pin is HIGH. This means the device is connected and is in data mode. Can not send commands."));
break;
case Disconnected:
Serial.println(F("The signal on the state pin is LOW. This means the device is not connected and is in command mode."));
break;
case Blinking:
Serial.println(F("The signal on the state pin is \"blinking\". This usually means the device is not connected and is in command mode."));
Serial.println(F("Consider reconfiguring the device to pass the state as-is. So it is simple to detect the state."));
break;
case NoStatePin:
Serial.println(F("No state pin was defined, can't detect connection state programmatically."));
break;
default:
Serial.println(F("Unknown connection state."));
break;
}
}
ModuleType identifyDevice()
{
Serial.println(F("Detecting module type"));
// check for HM-10
ble->print("AT");
String s = ble->readString();
if (s.length() == 2 && s.compareTo("OK")==0)
{
Serial.println(F("HM-10 detected!"));
return HM10;
}
else if (s.length() == 0)
{
// check for CC41
ble->println("");
s = ble->readString();
if (s.length() == 4 && s.compareTo("OK\r\n") == 0)
{
Serial.println(F("CC41 detected!")); // An HM-10 clone
return CC41;
}
else if (s.length() == 0)
{
// check for MLT_BT05
ble->println("AT");
s = ble->readString();
if (s.length() == 4 && s.compareTo("OK\r\n") == 0)
{
Serial.println(F("MLT-BT05 detected!")); // An HM-10 clone
return MLT_BT05;
}
else if (s.length() == 0)
{
Serial.println(F("No response received from module."));
Serial.println(F("Verify that the module is powered. Is the led blinking?"));
Serial.println(F("Check that pins are correctly connected and the right values were entered."));
Serial.println(F("Are you using a logic voltage converter for a module that already has such logic on board?"));
Serial.println(F("Are you using a module that expects 3.3v logic? You might need to do logic convertion on Arduio's TX pin (module's RX pin)."));
}
else
{
Serial.print(F("Unexpected result of length="));
Serial.println(s.length());
Serial.println(s);
}
}
else
{
Serial.print(F("Unexpected result of length="));
Serial.println(s.length());
Serial.println(s);
}
}
else
{
Serial.print(F("Unexpected result of length="));
Serial.println(s.length());
Serial.println(s);
}
return Unknown;
}
void displayMainSettings()
{
if (moduleType == HM10)
{
doCommandAndEchoResult(("AT+HELP?"));
doCommandAndEchoResult(("AT+VERS?"));
doCommandAndEchoResult(("AT+NAME?"));
doCommandAndEchoResult(("AT+PASS?"));
doCommandAndEchoResult(("AT+ADDR?"));
doCommandAndEchoResult(("AT+ROLE?"), F("Peripheral=0, Central=1"));
doCommandAndEchoResult(("AT+POWE?"), F("0 = -23dbm, 1 = -6dbm, 2 = 0dbm, 3 = 6dbm"));
doCommandAndEchoResult(("AT+TYPE?"), F("0 = Not need PIN Code, 1 = Auth not need PIN, 2 = Auth with PIN, 3 = Auth and bond"));
doCommandAndEchoResult(("AT+MODE?"), F("Transmission Mode=0, PIO collection Mode=1, Remote Control Mode=2"));
doCommandAndEchoResult(("AT+PIO1?"), F("Behavior of state pin, Blink on disconnect=0, Off on disconnect=1"));
}
else if (moduleType == CC41)
{
doCommandAndEchoResult(("AT+HELP"));
doCommandAndEchoResult(("AT+VERSION"));
doCommandAndEchoResult(("AT+NAME"));
doCommandAndEchoResult(("AT+PASS"));
doCommandAndEchoResult(("AT+ADDR"));
doCommandAndEchoResult(("AT+ROLE"));
doCommandAndEchoResult(("AT+POWE"), F("0 = -23dbm, 1 = -6dbm, 2 = 0dbm, 3 = 6dbm"));
doCommandAndEchoResult(("AT+TYPE"), F("0 = No binding, 3 = Do binding (not documented)"));
}
else if (moduleType == MLT_BT05)
{
doCommandAndEchoResult(("AT+HELP"));
doCommandAndEchoResult(("AT+VERSION"));
doCommandAndEchoResult(("AT+NAME"));
doCommandAndEchoResult(("AT+PIN"));
doCommandAndEchoResult(("AT+LADDR"));
doCommandAndEchoResult(("AT+ROLE"), F("0 = Slave, 1 = Master"));
doCommandAndEchoResult(("AT+POWE"), F("0 = -23dbm, 1 = -6dbm, 2 = 0dbm, 3 = 6dbm"));
doCommandAndEchoResult(("AT+TYPE"), F("0 = No password, 1 = Password pairing, 2 = Password pairing and binding, 3 = Not documented"));
}
}
Operation getMenuSelection()
{
Serial.println(F("0) Quit"));
Serial.println(F("1) Set module name"));
Serial.println(F("2) Set module password"));
if(moduleType==HM10)
Serial.println(F("3) Set module state pin behavior"));
Serial.println(F("4) Set module power"));
Serial.println(F("5) Set module binding type"));
Serial.println(F("6) Display main settings"));
Serial.println(F("7) Restore default settings"));
Serial.println(F("8) Reboot/reset/restart"));
Serial.println(F("9) Re-identify module"));
Serial.println(F("10) Detect connection state"));
Serial.println(F("11) Set role"));
Serial.println(F("12) Send custom command"));
int op = readInt(F("Enter menu selection"), 0);
return (Operation)(op);
}
void setName()
{
String val = readString(F("Enter new name"), F("HMSoft"));
String command(F("AT+NAME"));
command += val;
doCommandAndEchoResult(command.c_str());
}
void setPass()
{
String val = readString(F("Enter new pass/pin"), F("000000"));
String command(moduleType ==MLT_BT05? F("AT+PIN") : F("AT+PASS"));
command += val;
doCommandAndEchoResult(command.c_str());
}
void setStateBehavior()
{
String val = readString(F("Enter new state pin behavior 0 or 1"), F("1"));
String command(F("AT+PIO1"));
command += val;
doCommandAndEchoResult(command.c_str());
doCommandAndEchoResult("AT+RESET", F("to apply the AT+PIO1 command"));
}
void setPower()
{
int dbm = readInt(F("Enter new module power (0 = -23dbm, 1 = -6dbm, 2 = 0dbm, 3 = 6dbm)"), 2); // 2 is the default
String command(F("AT+POWE"));
command += dbm;
doCommandAndEchoResult(command.c_str());
}
void setRole()
{
int val = readInt(F("Enter new role (0 = Slave, 1 = Master)"), 0); // 0 is the default
String command(F("AT+ROLE"));
command += val;
doCommandAndEchoResult(command.c_str());
}
void sendCommand()
{
String command = readString(F("Enter a command"),F("AT")); // AT is the default
doCommandAndEchoResult(command.c_str());
}
void setBindingType()
{
int dbm = readInt(F("Enter new module binding type (options depend on module, see printout of main settings)"), 0); // 0 is the default
String command(F("AT+TYPE"));
command += dbm;
doCommandAndEchoResult(command.c_str());
}
void restoreDefaults()
{
doCommandAndEchoResult(moduleType == HM10?"AT+RENEW":"AT+DEFAULT");
}
void reboot()
{
doCommandAndEchoResult("AT+RESET");
}
/// Interface helper functions
int getLengthWithoutTerminator(String & s)
{
int l = s.length();
if (l == 0)
return 0;
if (s.charAt(l - 1) == '\r')
return l - 1;
return l;
}
int readInt(const __FlashStringHelper * message, int defaultValue)
{
Serial.print(message);
Serial.print(" [");
Serial.print(defaultValue);
Serial.println("] :");
String s = Serial.readStringUntil('\n');
if (getLengthWithoutTerminator(s) == 0)
return defaultValue;
return s.toInt();
}
String readString(const __FlashStringHelper * message, const __FlashStringHelper * defaultValue)
{
Serial.print(message);
Serial.print(" [");
Serial.print(defaultValue);
Serial.println("] :");
String s = Serial.readStringUntil('\n');
int l = getLengthWithoutTerminator(s);
if (l == 0)
return defaultValue;
s.remove(l); // remove terminator
return s;
}
void doCommandAndEchoResult(const char * command, const __FlashStringHelper * meaning)
{
// announce command
Serial.print(F("Sending command: "));
Serial.print(command);
if (meaning != NULL)
{
Serial.print(F(" ("));
Serial.print(meaning);
Serial.print(F(")"));
}
Serial.println();
// send command
if (moduleType == HM10)
ble->print(command);
else
ble->println(command);
// read and return response
// don't use "readString", it can't handle long and slow responses (such as AT+HELP) well
byte b;
while (ble->readBytes(&b, 1) == 1) // use "readBytes" and not "read" due to the timeout support
Serial.write(b);
// normalize line end
if (moduleType == HM10)
Serial.println();
}
void loop()
{
}