-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathA6lib.cpp
505 lines (385 loc) · 12.7 KB
/
A6lib.cpp
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
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
#include <Arduino.h>
#include <SoftwareSerial.h>
#include "A6lib.h"
#ifdef ESP8266
#define min _min
#define max _max
#endif
/////////////////////////////////////////////
// Public methods.
//
A6lib::A6lib(int transmitPin, int receivePin) {
#ifdef ESP8266
A6conn = new SoftwareSerial(receivePin, transmitPin, false, 1024);
#else
A6conn = new SoftwareSerial(receivePin, transmitPin, false);
#endif
A6conn->setTimeout(100);
}
A6lib::~A6lib() {
delete A6conn;
}
// Block until the module is ready.
byte A6lib::blockUntilReady(long baudRate) {
byte response = A6_NOTOK;
while (A6_OK != response) {
response = begin(baudRate);
// This means the modem has failed to initialize and we need to reboot
// it.
if (A6_FAILURE == response) {
return A6_FAILURE;
}
delay(1000);
logln("Waiting for module to be ready...");
}
return A6_OK;
}
// Initialize the software serial connection and change the baud rate from the
// default (autodetected) to the desired speed.
byte A6lib::begin(long baudRate) {
A6conn->flush();
if (A6_OK != setRate(baudRate)) {
return A6_NOTOK;
}
// Factory reset.
A6command("AT&F", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
// Echo off.
A6command("ATE0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
// Switch audio to headset.
enableSpeaker(0);
// Set caller ID on.
A6command("AT+CLIP=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
// Set SMS to text mode.
A6command("AT+CMGF=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
// Turn SMS indicators off.
A6command("AT+CNMI=1,0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
// Set SMS storage to the GSM modem. If this doesn't work for you, try changing the command to:
// "AT+CPMS=SM,SM,SM"
if (A6_OK != A6command("AT+CPMS=ME,ME,ME", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL))
// This may sometimes fail, in which case the modem needs to be
// rebooted.
{
return A6_FAILURE;
}
// Set SMS character set.
setSMScharset("UCS2");
return A6_OK;
}
// Reboot the module by setting the specified pin HIGH, then LOW. The pin should
// be connected to a P-MOSFET, not the A6's POWER pin.
void A6lib::powerCycle(int pin) {
logln("Power-cycling module...");
powerOff(pin);
delay(2000);
powerOn(pin);
// Give the module some time to settle.
logln("Done, waiting for the module to initialize...");
delay(20000);
logln("Done.");
A6conn->flush();
}
// Turn the modem power completely off.
void A6lib::powerOff(int pin) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
// Turn the modem power on.
void A6lib::powerOn(int pin) {
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
// Dial a number.
void A6lib::dial(String number) {
char buffer[50];
logln("Dialing number...");
sprintf(buffer, "ATD%s;", number.c_str());
A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Redial the last number.
void A6lib::redial() {
logln("Redialing last number...");
A6command("AT+DLST", "OK", "CONNECT", A6_CMD_TIMEOUT, 2, NULL);
}
// Answer a call.
void A6lib::answer() {
A6command("ATA", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Hang up the phone.
void A6lib::hangUp() {
A6command("ATH", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Check whether there is an active call.
callInfo A6lib::checkCallStatus() {
char number[50];
String response = "";
uint32_t respStart = 0;
callInfo cinfo = (const struct callInfo) {
0
};
// Issue the command and wait for the response.
A6command("AT+CLCC", "OK", "+CLCC", A6_CMD_TIMEOUT, 2, &response);
// Parse the response if it contains a valid +CLCC.
respStart = response.indexOf("+CLCC");
if (respStart >= 0) {
sscanf(response.substring(respStart).c_str(), "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", &cinfo.index, &cinfo.direction, &cinfo.state, &cinfo.mode, &cinfo.multiparty, number, &cinfo.type);
cinfo.number = String(number);
}
uint8_t comma_index = cinfo.number.indexOf('"');
if (comma_index != -1) {
logln("Extra comma found.");
cinfo.number = cinfo.number.substring(0, comma_index);
}
return cinfo;
}
// Get the strength of the GSM signal.
int A6lib::getSignalStrength() {
String response = "";
uint32_t respStart = 0;
int strength, error = 0;
// Issue the command and wait for the response.
A6command("AT+CSQ", "OK", "+CSQ", A6_CMD_TIMEOUT, 2, &response);
respStart = response.indexOf("+CSQ");
if (respStart < 0) {
return 0;
}
sscanf(response.substring(respStart).c_str(), "+CSQ: %d,%d",
&strength, &error);
// Bring value range 0..31 to 0..100%, don't mind rounding..
strength = (strength * 100) / 31;
return strength;
}
// Get the real time from the modem. Time will be returned as yy/MM/dd,hh:mm:ss+XX
String A6lib::getRealTimeClock() {
String response = "";
// Issue the command and wait for the response.
A6command("AT+CCLK?", "OK", "yy", A6_CMD_TIMEOUT, 1, &response);
int respStart = response.indexOf("+CCLK: \"") + 8;
response.setCharAt(respStart - 1, '-');
return response.substring(respStart, response.indexOf("\""));
}
// Send an SMS.
byte A6lib::sendSMS(String number, String text) {
char ctrlZ[2] = { 0x1a, 0x00 };
char buffer[100];
if (text.length() > 159) {
// We can't send messages longer than 160 characters.
return A6_NOTOK;
}
log("Sending SMS to ");
log(number);
logln("...");
sprintf(buffer, "AT+CMGS=\"%s\"", number.c_str());
A6command(buffer, ">", "yy", A6_CMD_TIMEOUT, 2, NULL);
delay(100);
A6conn->println(text.c_str());
A6conn->println(ctrlZ);
A6conn->println();
return A6_OK;
}
// Retrieve the number and locations of unread SMS messages.
int A6lib::getUnreadSMSLocs(int* buf, int maxItems) {
return getSMSLocsOfType(buf, maxItems, "REC UNREAD");
}
// Retrieve the number and locations of all SMS messages.
int A6lib::getSMSLocs(int* buf, int maxItems) {
return getSMSLocsOfType(buf, maxItems, "ALL");
}
// Retrieve the number and locations of all SMS messages.
int A6lib::getSMSLocsOfType(int* buf, int maxItems, String type) {
String seqStart = "+CMGL: ";
String response = "";
String command = "AT+CMGL=\"";
command += type;
command += "\"";
// Issue the command and wait for the response.
A6command(command.c_str(), "\xff\r\nOK\r\n", "\r\nOK\r\n", A6_CMD_TIMEOUT, 2, &response);
int seqStartLen = seqStart.length();
int responseLen = response.length();
int index, occurrences = 0;
// Start looking for the +CMGL string.
for (int i = 0; i < (responseLen - seqStartLen); i++) {
// If we found a response and it's less than occurrences, add it.
if (response.substring(i, i + seqStartLen) == seqStart && occurrences < maxItems) {
// Parse the position out of the reply.
sscanf(response.substring(i, i + 12).c_str(), "+CMGL: %u,%*s", &index);
buf[occurrences] = index;
occurrences++;
}
}
return occurrences;
}
// Return the SMS at index.
SMSmessage A6lib::readSMS(int index) {
String response = "";
char buffer[30];
// Issue the command and wait for the response.
sprintf(buffer, "AT+CMGR=%d", index);
A6command(buffer, "\xff\r\nOK\r\n", "\r\nOK\r\n", A6_CMD_TIMEOUT, 2, &response);
char number[50];
char date[50];
char type[10];
int respStart = 0;
SMSmessage sms = (const struct SMSmessage) {
"", "", ""
};
// Parse the response if it contains a valid +CLCC.
respStart = response.indexOf("+CMGR");
if (respStart >= 0) {
// Parse the message header.
sscanf(response.substring(respStart).c_str(), "+CMGR: \"REC %s\",\"%s\",,\"%s\"\r\n", type, number, date);
sms.number = String(number);
sms.date = String(date);
// The rest is the message, extract it.
sms.message = response.substring(strlen(type) + strlen(number) + strlen(date) + 24, response.length() - 8);
}
return sms;
}
// Delete the SMS at index.
byte A6lib::deleteSMS(int index) {
char buffer[20];
sprintf(buffer, "AT+CMGD=%d", index);
return A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Delete SMS with special flags; example 1,4 delete all SMS from the storage area
byte A6lib::deleteSMS(int index, int flag) {
String command = "AT+CMGD=";
command += String(index);
command += ",";
command += String(flag);
return A6command(command.c_str(), "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Set the SMS charset.
byte A6lib::setSMScharset(String charset) {
char buffer[30];
sprintf(buffer, "AT+CSCS=\"%s\"", charset.c_str());
return A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Set the volume for the speaker. level should be a number between 5 and
// 8 inclusive.
void A6lib::setVol(byte level) {
char buffer[30];
// level should be between 5 and 8.
level = min(max(level, 5), 8);
sprintf(buffer, "AT+CLVL=%d", level);
A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
// Enable the speaker, rather than the headphones. Pass 0 to route audio through
// headphones, 1 through speaker.
void A6lib::enableSpeaker(byte enable) {
char buffer[30];
// enable should be between 0 and 1.
enable = min(max(enable, 0), 1);
sprintf(buffer, "AT+SNFS=%d", enable);
A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}
/////////////////////////////////////////////
// Private methods.
//
// Autodetect the connection rate.
long A6lib::detectRate() {
unsigned long rate = 0;
unsigned long rates[] = {9600, 115200};
// Try to autodetect the rate.
logln("Autodetecting connection rate...");
for (int i = 0; i < countof(rates); i++) {
rate = rates[i];
A6conn->begin(rate);
log("Trying rate ");
log(rate);
logln("...");
delay(100);
if (A6command("\rAT", "OK", "+CME", 2000, 2, NULL) == A6_OK) {
return rate;
}
}
logln("Couldn't detect the rate.");
return A6_NOTOK;
}
// Set the A6 baud rate.
char A6lib::setRate(long baudRate) {
int rate = 0;
rate = detectRate();
if (rate == A6_NOTOK) {
return A6_NOTOK;
}
// The rate is already the desired rate, return.
//if (rate == baudRate) return OK;
logln("Setting baud rate on the module...");
// Change the rate to the requested.
char buffer[30];
sprintf(buffer, "AT+IPR=%d", baudRate);
A6command(buffer, "OK", "+IPR=", A6_CMD_TIMEOUT, 3, NULL);
logln("Switching to the new rate...");
// Begin the connection again at the requested rate.
A6conn->begin(baudRate);
logln("Rate set.");
return A6_OK;
}
// Read some data from the A6 in a non-blocking manner.
String A6lib::read() {
String reply = "";
if (A6conn->available()) {
reply = A6conn->readString();
}
// XXX: Replace NULs with \xff so we can match on them.
for (int x = 0; x < reply.length(); x++) {
if (reply.charAt(x) == 0) {
reply.setCharAt(x, 255);
}
}
return reply;
}
// Issue a command.
byte A6lib::A6command(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) {
byte returnValue = A6_NOTOK;
byte count = 0;
// Get rid of any buffered output.
A6conn->flush();
while (count < repetitions && returnValue != A6_OK) {
log("Issuing command: ");
logln(command);
A6conn->write(command);
A6conn->write('\r');
if (A6waitFor(resp1, resp2, timeout, response) == A6_OK) {
returnValue = A6_OK;
} else {
returnValue = A6_NOTOK;
}
count++;
}
return returnValue;
}
// Wait for responses.
byte A6lib::A6waitFor(const char *resp1, const char *resp2, int timeout, String *response) {
unsigned long entry = millis();
String reply = "";
byte retVal = 99;
do {
reply += read();
#ifdef ESP8266
yield();
#endif
} while (((reply.indexOf(resp1) + reply.indexOf(resp2)) == -2) && ((millis() - entry) < timeout));
if (reply != "") {
log("Reply in ");
log((millis() - entry));
log(" ms: ");
logln(reply);
}
if (response != NULL) {
*response = reply;
}
if ((millis() - entry) >= timeout) {
retVal = A6_TIMEOUT;
logln("Timed out.");
} else {
if (reply.indexOf(resp1) + reply.indexOf(resp2) > -2) {
logln("Reply OK.");
retVal = A6_OK;
} else {
logln("Reply NOT OK.");
retVal = A6_NOTOK;
}
}
return retVal;
}