SIM900
sim900.cpp
1/*
2 * This file is part of the SIM900 Arduino Shield library.
3 * Copyright (c) 2023 Nathanne Isip
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#include "sim900.h"
25
26void SIM900::sendCommand(String message) {
27 this->sim900.println(message);
28}
29
30String SIM900::getResponse() {
31 delay(500);
32
33 if(this->sim900.available() > 0) {
34 String response = this->sim900.readString();
35 response.trim();
36
37 return response;
38 }
39
40 return "";
41}
42
43String SIM900::getReturnedMode() {
44 String response = this->getResponse();
45 return response.substring(response.lastIndexOf('\n') + 1);
46}
47
48bool SIM900::isSuccessCommand() {
49 return this->getReturnedMode() == F("OK");
50}
51
52String SIM900::rawQueryOnLine(uint16_t line) {
53 String response = this->getResponse();
54 String result = "";
55
56 uint16_t currentLine = 0;
57 for(int i = 0; i < response.length(); i++)
58 if(currentLine == line && response[i] != '\n')
59 result += response[i];
60 else if(response[i] == '\n') {
61 currentLine++;
62
63 if(currentLine > line)
64 break;
65 }
66
67 return result;
68}
69
70String SIM900::queryResult() {
71 String response = this->getResponse();
72 String result = F("");
73
74 int idx = response.indexOf(": ");
75 if(idx != -1)
76 result = response.substring(
77 idx + 2,
78 response.indexOf('\n', idx)
79 );
80
81 return result;
82}
83
84SIM900::SIM900(Stream& _sim900):sim900(_sim900){}
85
87 this->sendCommand(F("AT"));
88 return this->isSuccessCommand();
89}
90
92 this->sendCommand(F("AT+CPIN?"));
93 return this->isSuccessCommand();
94}
95
96bool SIM900::changeCardPin(uint8_t pin) {
97 if(pin > 9999)
98 return false;
99
100 this->sendCommand("AT+CPIN=\"" + String(pin) + "\"");
101 return this->isSuccessCommand();
102}
103
106 signal.rssi = signal.bit_error_rate = 0;
107 this->sendCommand("AT+CSQ");
108
109 String response = this->queryResult();
110 uint8_t delim = response.indexOf(',');
111
112 if(delim == -1)
113 return signal;
114
115 signal.rssi = (uint8_t) response.substring(0, delim).toInt();
116 signal.bit_error_rate = (uint8_t) response.substring(delim + 1).toInt();
117
118 return signal;
119}
120
121// void SIM900::close() {
122// this->sim900->end();
123// }
124
125SIM900DialResult SIM900::dialUp(String number) {
126 this->sendCommand("ATD+ " + number + ";");
127
128 SIM900DialResult result = SIM900_DIAL_RESULT_ERROR;
129 String mode = this->getReturnedMode();
130
131 if(mode == F("NO DIALTONE"))
132 result = SIM900_DIAL_RESULT_NO_DIALTONE;
133 else if(mode == F("BUSY"))
134 result = SIM900_DIAL_RESULT_BUSY;
135 else if(mode == F("NO CARRIER"))
136 result = SIM900_DIAL_RESULT_NO_CARRIER;
137 else if(mode == F("NO ANSWER"))
138 result = SIM900_DIAL_RESULT_NO_ANSWER;
139 else if(mode == F("OK"))
140 result = SIM900_DIAL_RESULT_OK;
141
142 return result;
143}
144
145SIM900DialResult SIM900::redialUp() {
146 this->sendCommand(F("ATDL"));
147
148 SIM900DialResult result = SIM900_DIAL_RESULT_ERROR;
149 String mode = this->getReturnedMode();
150
151 if(mode == F("NO DIALTONE"))
152 result = SIM900_DIAL_RESULT_NO_DIALTONE;
153 else if(mode == F("BUSY"))
154 result = SIM900_DIAL_RESULT_BUSY;
155 else if(mode == F("NO CARRIER"))
156 result = SIM900_DIAL_RESULT_NO_CARRIER;
157 else if(mode == F("NO ANSWER"))
158 result = SIM900_DIAL_RESULT_NO_ANSWER;
159 else if(mode == F("OK"))
160 result = SIM900_DIAL_RESULT_OK;
161
162 return result;
163}
164
165SIM900DialResult SIM900::acceptIncomingCall() {
166 this->sendCommand(F("ATA"));
167
168 SIM900DialResult result = SIM900_DIAL_RESULT_ERROR;
169 String mode = this->getReturnedMode();
170
171 if(mode == F("NO CARRIER"))
172 result = SIM900_DIAL_RESULT_NO_CARRIER;
173 else if(mode == F("OK"))
174 result = SIM900_DIAL_RESULT_OK;
175
176 return result;
177}
178
180 this->sendCommand(F("ATH"));
181 return this->isSuccessCommand();
182}
183
184bool SIM900::sendSMS(String number, String message) {
185 this->handshake();
186
187 this->sendCommand(F("AT+CMGF=1"));
188 delay(500);
189 this->sendCommand("AT+CMGS=\"" + number + "\"");
190 delay(500);
191 this->sendCommand(message);
192 delay(500);
193 this->sim900.write(0x1a);
194
195 return this->getReturnedMode().startsWith(">");
196}
197
199 SIM900Operator simOperator;
200 simOperator.mode = static_cast<SIM900OperatorMode>(0);
201 simOperator.format = static_cast<SIM900OperatorFormat>(0);
202 simOperator.name = "";
203
204 this->sendCommand(F("AT+COPS?"));
205
206 String response = this->queryResult();
207 uint8_t delim1 = response.indexOf(','),
208 delim2 = response.indexOf(',', delim1 + 1);
209
210 simOperator.mode = intToSIM900OperatorMode((uint8_t) response.substring(0, delim1).toInt());
211 simOperator.format = intToSIM900OperatorFormat((uint8_t) response.substring(delim1 + 1, delim2).toInt());
212 simOperator.name = response.substring(delim2 + 2, response.length() - 2);
213
214 return simOperator;
215}
216
218 this->sendCommand(F("AT+CMGF=1"));
219 if(!this->isSuccessCommand())
220 return false;
221
222 this->sendCommand(F("AT+CGATT=1"));
223 if(!this->isSuccessCommand())
224 return false;
225
226 this->sendCommand(
227 "AT+CSTT=\"" + apn.apn +
228 "\",\"" + apn.username +
229 "\",\"" + apn.password + "\""
230 );
231
232 return (this->hasAPN = this->isSuccessCommand());
233}
234
236 if(!this->hasAPN)
237 return false;
238
239 this->sendCommand(F("AT+CIICR"));
240 delay(1000);
241
242 return this->isSuccessCommand();
243}
244
246 SIM900HTTPResponse response;
247 response.status = -1;
248
249 if(!this->hasAPN)
250 return response;
251
252 this->sendCommand(
253 "AT+CIPSTART=\"TCP\",\"" + request.domain +
254 "\"," + String(request.port)
255 );
256
257 String resp = this->getResponse();
258 resp.trim();
259
260 delay(1500);
261 if(!resp.endsWith(F("CONNECT OK")))
262 return response;
263
264 String requestStr = request.method + " " +
265 request.resource + " HTTP/1.0\r\nHost: " +
266 request.domain + "\r\n";
267
268 for(int i = 0; i < request.header_count; i++)
269 requestStr += request.headers[i].key + ": " +
270 request.headers[i].value + "\r\n";
271
272 if(request.data != "" || request.data != NULL)
273 requestStr += request.data + "\r\n";
274
275 requestStr += F("\r\n");
276 this->sendCommand(requestStr);
277
278 // TODO
279 return response;
280}
281
283 this->sendCommand(
284 "AT+CCLK=\"" + String(config.year <= 9 ? "0" : "") + String(config.year) +
285 "/" + String(config.month <= 9 ? "0" : "") + String(config.month) +
286 "/" + String(config.day <= 9 ? "0" : "") + String(config.day) +
287 "," + String(config.hour <= 9 ? "0" : "") + String(config.hour) +
288 ":" + String(config.minute <= 9 ? "0" : "") + String(config.minute) +
289 ":" + String(config.second <= 9 ? "0" : "") + String(config.second) +
290 "+" + String(config.gmt <= 9 ? "0" : "") + String(config.gmt) + "\""
291 );
292
293 return this->isSuccessCommand();
294}
295
298 rtc.year = rtc.month = rtc.day =
299 rtc.hour = rtc.minute = rtc.second =
300 rtc.gmt = 0;
301
302 this->sendCommand(F("AT+CMGF=1"));
303 if(!this->isSuccessCommand())
304 return rtc;
305
306 this->sendCommand(F("AT+CENG=3"));
307 if(!this->isSuccessCommand())
308 return rtc;
309 this->sendCommand(F("AT+CCLK?"));
310
311 String time = this->queryResult();
312 time = time.substring(1, time.length() - 2);
313
314 uint8_t delim1 = time.indexOf('/'),
315 delim2 = time.indexOf('/', delim1 + 1),
316 delim3 = time.indexOf(',', delim2),
317 delim4 = time.indexOf(':', delim3),
318 delim5 = time.indexOf(':', delim4 + 1),
319 delim6 = time.indexOf('+', delim5);
320
321 rtc.year = (uint8_t) time.substring(0, delim1).toInt();
322 rtc.month = (uint8_t) time.substring(delim1 + 1, delim2).toInt();
323 rtc.day = (uint8_t) time.substring(delim2 + 1, delim3).toInt();
324 rtc.hour = (uint8_t) time.substring(delim3 + 1, delim4).toInt();
325 rtc.minute = (uint8_t) time.substring(delim4 + 1, delim5).toInt();
326 rtc.second = (uint8_t) time.substring(delim5 + 1, delim6).toInt();
327 rtc.gmt = (uint8_t) time.substring(delim6 + 1).toInt();
328
329 return rtc;
330}
331
332bool SIM900::savePhonebook(uint8_t index, SIM900CardAccount account) {
333 this->sendCommand(
334 "AT+CPBW=" + String(index) +
335 ",\"" + account.number +
336 "\"," + account.numberType +
337 ",\"" + account.name + "\""
338 );
339 return this->isSuccessCommand();
340}
341
343 this->sendCommand("AT+CPBR=" + String(index));
344
345 SIM900CardAccount accountInfo;
346 accountInfo.numberType = static_cast<SIM900PhonebookType>(0);
347
348 String response = this->queryResult();
349 response = response.substring(response.indexOf(',') + 1);
350
351 uint8_t delim1 = response.indexOf(','),
352 delim2 = response.indexOf(',', delim1 + 1);
353
354 accountInfo.number = response.substring(1, delim1 - 1);
355
356 uint8_t type = (uint8_t) response.substring(delim1 + 1, delim2).toInt();
357 if(type == 129 || type == 145)
358 accountInfo.numberType = static_cast<SIM900PhonebookType>(type);
359 else accountInfo.numberType = static_cast<SIM900PhonebookType>(0);
360
361 accountInfo.name = response.substring(delim2 + 2, response.length() - 2);
362 return accountInfo;
363}
364
365bool SIM900::deletePhonebook(uint8_t index) {
366 this->sendCommand("AT+CPBW=" + String(index));
367 return this->isSuccessCommand();
368}
369
372 capacity.used = capacity.max = 0;
373 capacity.memoryType = F("");
374
375 this->sendCommand("AT+CPBS?");
376
377 String response = this->queryResult();
378 uint8_t delim1 = response.indexOf(','),
379 delim2 = response.indexOf(',', delim1 + 1);
380
381 capacity.memoryType = response.substring(1, delim1 - 1);
382 capacity.used = (uint8_t) response.substring(delim1 + 1, delim2).toInt();
383 capacity.max = (uint8_t) response.substring(delim2 + 1).toInt();
384
385 return capacity;
386}
387
389 this->sendCommand(F("AT+CNUM"));
390
391 SIM900CardAccount account;
392 account.name = F("");
393
394 String response = this->queryResult();
395 if(response == F(""))
396 return account;
397
398 uint8_t delim1 = response.indexOf(','),
399 delim2 = response.indexOf(',', delim1 + 1),
400 delim3 = response.indexOf(',', delim2 + 1),
401 delim4 = response.indexOf(',', delim3 + 1);
402
403 account.name = response.substring(1, delim1 - 1);
404 account.number = response.substring(delim1 + 2, delim2 - 1);
405 account.type = (uint8_t) response.substring(delim2 + 1, delim3).toInt();
406 account.speed = (uint8_t) response.substring(delim3 + 1, delim4).toInt();
407 account.service = intToSIM900CardService((uint8_t) response.substring(delim4 + 1).toInt());
408 account.numberType = static_cast<SIM900PhonebookType>(0);
409
410 return account;
411}
412
414 this->sendCommand(F("AT+GMI"));
415 return this->rawQueryOnLine(2);
416}
417
419 this->sendCommand(F("AT+GMR"));
420
421 String result = this->rawQueryOnLine(2);
422 result = result.substring(result.lastIndexOf(F(":")) + 1);
423
424 return result;
425}
426
427String SIM900::imei() {
428 this->sendCommand(F("AT+GSN"));
429 return this->rawQueryOnLine(2);
430}
431
433 this->sendCommand(F("AT+GMM"));
434 return this->rawQueryOnLine(2);
435}
436
438 this->sendCommand(F("AT+GOI"));
439 return this->rawQueryOnLine(2);
440}
441
443 this->sendCommand(F("AT+CIFSR"));
444 return this->rawQueryOnLine(2);
445}
bool savePhonebook(uint8_t index, SIM900CardAccount account)
Save a contact in the SIM card's phonebook.
Definition: sim900.cpp:332
String manufacturer()
Get the manufacturer name of the SIM900 module.
Definition: sim900.cpp:413
SIM900DialResult redialUp()
Redial the last outgoing call.
Definition: sim900.cpp:145
String chipName()
Get the chip name of the SIM900 module.
Definition: sim900.cpp:437
SIM900PhonebookCapacity phonebookCapacity()
Get information about the capacity of the SIM card's phonebook.
Definition: sim900.cpp:370
SIM900DialResult acceptIncomingCall()
Accept an incoming call.
Definition: sim900.cpp:165
SIM900DialResult dialUp(String number)
Initiate an outgoing call to a phone number.
Definition: sim900.cpp:125
bool enableGPRS()
Enable the General Packet Radio Service (GPRS) for data communication.
Definition: sim900.cpp:235
String ipAddress()
Get the IP address assigned to the SIM900 module.
Definition: sim900.cpp:442
SIM900CardAccount cardNumber()
Get the SIM card number.
Definition: sim900.cpp:388
String chipModel()
Get the chip model of the SIM900 module.
Definition: sim900.cpp:432
bool deletePhonebook(uint8_t index)
Delete a contact from the SIM card's phonebook.
Definition: sim900.cpp:365
SIM900Operator networkOperator()
Get information about the current network operator.
Definition: sim900.cpp:198
bool sendSMS(String number, String message)
Send an SMS (Short Message Service).
Definition: sim900.cpp:184
bool updateRtc(SIM900RTC config)
Update the SIM900 module's real-time clock (RTC).
Definition: sim900.cpp:282
String softwareRelease()
Get the software release version of the SIM900 module.
Definition: sim900.cpp:418
bool hangUp()
Hang up an active call.
Definition: sim900.cpp:179
bool connectAPN(SIM900APN apn)
Connect to an Access Point Name (APN) for mobile data.
Definition: sim900.cpp:217
bool changeCardPin(uint8_t pin)
Change the PIN code of the SIM card.
Definition: sim900.cpp:96
SIM900(Stream &_sim900)
Constructor for the SIM900 class.
Definition: sim900.cpp:84
SIM900RTC rtc()
Get the real-time clock (RTC) information.
Definition: sim900.cpp:296
bool handshake()
Initialize communication with the SIM900 module and perform a handshake.
Definition: sim900.cpp:86
bool isCardReady()
Check if the SIM card is ready.
Definition: sim900.cpp:91
String imei()
Get the International Mobile Equipment Identity (IMEI) number of the SIM900 module.
Definition: sim900.cpp:427
SIM900CardAccount retrievePhonebook(uint8_t index)
Retrieve a contact from the SIM card's phonebook.
Definition: sim900.cpp:342
SIM900Signal signal()
Get the signal strength and bit error rate of the network connection.
Definition: sim900.cpp:104
SIM900HTTPResponse request(SIM900HTTPRequest request)
Send an HTTP request to a remote server.
Definition: sim900.cpp:245
A structure representing Access Point Name (APN) configuration for mobile data.
A structure representing a card account, including name, number, type, and service information.
A structure representing an HTTP request.
A structure representing an HTTP response.
A structure representing mobile network operator information.
A structure representing the capacity of a phonebook memory type.
A structure representing real-time clock (RTC) information.
A structure representing signal strength and bit error rate information.