Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
BG96.cpp
00001 /** 00002 * copyright (c) 2018-2019, James Flynn 00003 * SPDX-License-Identifier: Apache-2.0 00004 */ 00005 00006 /* 00007 * Licensed under the Apache License, Version 2.0 (the "License"); 00008 * you may not use this file except in compliance with the License. 00009 * You may obtain a copy of the License at 00010 * 00011 * http://www.apache.org/licenses/LICENSE-2.0 00012 * 00013 * Unless required by applicable law or agreed to in writing, software 00014 * distributed under the License is distributed on an "AS IS" BASIS, 00015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00016 * 00017 * See the License for the specific language governing permissions and 00018 * limitations under the License. 00019 */ 00020 00021 /** 00022 * @file BG96.cpp 00023 * @brief Implements a standard NetworkInterface class for use with Quicktel BG96 00024 * 00025 * @author James Flynn 00026 * 00027 * @date 19-Mar-2018 00028 * 00029 */ 00030 00031 #include <ctype.h> 00032 00033 #include "mbed.h" 00034 #include "mbed_debug.h" 00035 #include "BG96.h" 00036 00037 #define BG96_1s_WAIT 1000 //will wait for 1 second for startup 00038 #define BG96_60s_TO 60000 //wait 60 seconds 00039 #define BG96_150s_TO 150000 //wait 150s (2.5 mins) 00040 #define BG96_TX_TIMEOUT 2000 //time before a TX timeout occurs 00041 #define BG96_RX_TIMEOUT 1000 //time before a TX timeout occurs 00042 #define BG96_WAIT4READY 15000 //wait 15 seconds for 'RDY' after reset 00043 #define BG96_AT_TIMEOUT 1000 //standard AT command timeout 00044 #define BG96_WRK_CONTEXT 1 //we will only use context 1 in driver 00045 #define BG96_CLOSE_TO 1 //wait x seconds for a socket close 00046 00047 // 00048 // if DEBUG is enabled, these macros are used to dump data arrays 00049 // 00050 #if MBED_CONF_APP_BG96_DEBUG == true 00051 #define TOSTR(x) #x 00052 #define INTSTR(x) TOSTR(x) 00053 #define DUMP_LOC (char*)(__FILE__ ":" INTSTR(__LINE__)) 00054 00055 #define DUMP_ARRAY(x,s) { \ 00056 int i, k; \ 00057 for (i=0; i<s; i+=16) { \ 00058 printf("[%s]:0x%04X: ",DUMP_LOC,i); \ 00059 for (k=0; k<16; k++) { \ 00060 if( (i+k)<s ) \ 00061 printf("%02X ", x[i+k]); \ 00062 else \ 00063 printf(" "); \ 00064 } \ 00065 printf(" "); \ 00066 for (k=0; k<16; k++) { \ 00067 if( (i+k)<s ) \ 00068 printf("%c", isprint(x[i+k])? x[i+k]:'.'); \ 00069 } \ 00070 printf("\n\r"); \ 00071 } \ 00072 } 00073 #else 00074 #define DUMP_ARRAY(x,s) /* not used */ 00075 #endif 00076 00077 /** ---------------------------------------------------------- 00078 * @brief constructor 00079 * @param none 00080 * @retval none 00081 */ 00082 BG96::BG96(bool debug) : 00083 _contextID(1), 00084 _serial(MBED_CONF_BG96_LIBRARY_BG96_TX, MBED_CONF_BG96_LIBRARY_BG96_RX), 00085 _parser(&_serial), 00086 _bg96_reset(MBED_CONF_BG96_LIBRARY_BG96_RESET), 00087 _vbat_3v8_en(MBED_CONF_BG96_LIBRARY_BG96_WAKE), 00088 _bg96_pwrkey(MBED_CONF_BG96_LIBRARY_BG96_PWRKEY) 00089 { 00090 _serial.set_baud(115200); 00091 _parser.debug_on(debug); 00092 _parser.set_timeout(BG96_AT_TIMEOUT); 00093 _parser.set_delimiter("\r\n"); 00094 } 00095 00096 BG96::~BG96(void) 00097 { } 00098 00099 /** ---------------------------------------------------------- 00100 * @brief get BG96 SW version 00101 * @param none 00102 * @retval string containing SW version 00103 */ 00104 const char* BG96::getRev(char* combined) 00105 { 00106 bool ok=false; 00107 char buf1[20], buf2[20]; 00108 00109 _bg96_mutex.lock(); 00110 ok = (tx2bg96((char*)"AT+CGMM") && _parser.recv("%s\n",buf1) && _parser.recv("OK") && 00111 _parser.send("AT+CGMR") && _parser.recv("%s\n",buf2) && _parser.recv("OK") ); 00112 _bg96_mutex.unlock(); 00113 00114 if( ok ) 00115 sprintf(combined,"%s Rev:%s",buf1,buf2); 00116 return ok? (const char*) combined : NULL; 00117 } 00118 00119 /** ---------------------------------------------------------- 00120 * @brief enable AT command tracing 00121 * @param integer, if msb is set, tracing enabled 00122 * @retval none 00123 */ 00124 void BG96::doDebug(int f) 00125 { 00126 _parser.debug_on(f&0x80); 00127 } 00128 00129 /** ---------------------------------------------------------- 00130 * @brief Tx a string to the BG96 and wait for an OK response 00131 * @param none 00132 * @retval true if OK received, false otherwise 00133 */ 00134 bool BG96::tx2bg96(char* cmd) { 00135 bool ok=false; 00136 _bg96_mutex.lock(); 00137 ok=_parser.send(cmd) && _parser.recv("OK"); 00138 _bg96_mutex.unlock(); 00139 return ok; 00140 } 00141 00142 /** ---------------------------------------------------------- 00143 * @brief set the contextID for the BG96. This context will 00144 * be used for all subsequent operations 00145 * @param int of desired context. if <1, return the current context 00146 * @retval current context 00147 */ 00148 /* 00149 * Context can be 1-16 00150 */ 00151 int BG96::setContext( int i ) 00152 { 00153 if( i > 16 ) 00154 return -1; 00155 00156 if( i < 1 ) 00157 return _contextID; 00158 00159 return _contextID = i; 00160 } 00161 00162 /** ---------------------------------------------------------- 00163 * @brief perform a HW reset of the BG96 00164 * @param none 00165 * @retval none 00166 */ 00167 void BG96::reset(void) 00168 { 00169 _bg96_reset = 0; 00170 _bg96_pwrkey = 0; 00171 _vbat_3v8_en = 0; 00172 wait_ms(300); 00173 00174 _bg96_reset = 1; 00175 _vbat_3v8_en = 1; 00176 _bg96_pwrkey = 1; 00177 wait_ms(400); 00178 00179 _bg96_reset = 0; 00180 wait_ms(10); 00181 } 00182 00183 /** ---------------------------------------------------------- 00184 * @brief wait for 'RDY' response from BG96 00185 * @param none 00186 * @retval true if 'RDY' received, false otherwise 00187 */ 00188 bool BG96::BG96Ready(void) 00189 { 00190 Timer t; 00191 int done=false; 00192 00193 _bg96_mutex.lock(); 00194 reset(); 00195 t.start(); 00196 while( !done && t.read_ms() < BG96_WAIT4READY ) 00197 done = _parser.recv("RDY"); 00198 _bg96_mutex.unlock(); 00199 return done; 00200 } 00201 00202 00203 /** ---------------------------------------------------------- 00204 * @brief startup BG96 module 00205 * @param none 00206 * @retval true if successful, false otherwise 00207 */ 00208 bool BG96::startup(void) 00209 { 00210 int done=false; 00211 00212 if( !BG96Ready() ) 00213 return false; 00214 00215 _bg96_mutex.lock(); 00216 _parser.set_timeout(BG96_1s_WAIT); 00217 if( tx2bg96((char*)"ATE0") ) 00218 done = tx2bg96((char*)"AT+COPS?"); 00219 _parser.set_timeout(BG96_AT_TIMEOUT); 00220 _bg96_mutex.unlock(); 00221 return done; 00222 } 00223 00224 00225 /** ---------------------------------------------------------- 00226 * @brief connect to APN 00227 * @param apn string 00228 * @param username (not used) 00229 * @param password (not used) 00230 * @retval nsapi_error_t 00231 */ 00232 nsapi_error_t BG96::connect(const char *apn, const char *username, const char *password) 00233 { 00234 char cmd[100],_apn[50]; 00235 bool done = false; 00236 Timer t; 00237 int cntx; 00238 00239 _bg96_mutex.lock(); 00240 t.start(); 00241 do { 00242 _parser.send("AT+QICSGP=%d",_contextID); 00243 done = _parser.recv("+QICSGP: %d, \"%50[^\"]\"",&cntx, _apn); 00244 wait_ms(2); 00245 } 00246 while( !done && t.read_ms() < BG96_60s_TO ); 00247 00248 if( !done ) { 00249 _bg96_mutex.unlock(); 00250 return NSAPI_ERROR_DEVICE_ERROR; 00251 } 00252 00253 _parser.flush(); 00254 if( strcmp(_apn,apn) ) { 00255 sprintf(cmd,"AT+QICSGP=%d,1,\"%s\",\"%s\",\"%s\",0", _contextID, &apn[0], &username[0], &password[0]); 00256 if( !tx2bg96(cmd) ) { 00257 _bg96_mutex.unlock(); 00258 return NSAPI_ERROR_DEVICE_ERROR; 00259 } 00260 } 00261 00262 sprintf(cmd,"AT+QIACT=%d", _contextID); 00263 t.reset(); 00264 done=false; 00265 while( !done && t.read_ms() < BG96_150s_TO ) 00266 done = tx2bg96(cmd); 00267 00268 _bg96_mutex.unlock(); 00269 00270 return done? NSAPI_ERROR_OK : NSAPI_ERROR_DEVICE_ERROR; 00271 } 00272 00273 /** ---------------------------------------------------------- 00274 * @brief disconnect from an APN 00275 * @param none 00276 * @retval true/false if disconnect was successful or not 00277 */ 00278 bool BG96::disconnect(void) 00279 { 00280 char buff[15]; 00281 _parser.set_timeout(BG96_60s_TO); 00282 sprintf(buff,"AT+QIDEACT=%d\r",_contextID); 00283 bool ok = tx2bg96(buff); 00284 _parser.set_timeout(BG96_AT_TIMEOUT); 00285 return ok; 00286 } 00287 00288 /** ---------------------------------------------------------- 00289 * @brief perform DNS lookup of URL to determine IP address 00290 * @param string containing the URL 00291 * @retval string containing the IP results from the URL DNS 00292 */ 00293 bool BG96::resolveUrl(const char *name, char* ipstr) 00294 { 00295 char buf2[50]; 00296 bool ok; 00297 int err, ipcount, dnsttl; 00298 00299 _bg96_mutex.lock(); 00300 _parser.set_timeout(BG96_60s_TO); 00301 ok = ( _parser.send("AT+QIDNSGIP=%d,\"%s\"",_contextID,name) 00302 && _parser.recv("OK") 00303 && _parser.recv("+QIURC: \"dnsgip\",%d,%d,%d",&err, &ipcount, &dnsttl) 00304 && err==0 00305 && ipcount > 0 00306 ); 00307 00308 if( ok ) { 00309 _parser.recv("+QIURC: \"dnsgip\",\"%[^\"]\"",ipstr); //use the first DNS value 00310 for( int i=0; i<ipcount-1; i++ ) 00311 _parser.recv("+QIURC: \"dnsgip\",\"%[^\"]\"", buf2); //and discrard the rest if >1 00312 } 00313 _parser.set_timeout(BG96_AT_TIMEOUT); 00314 _bg96_mutex.unlock(); 00315 00316 return ok; 00317 } 00318 00319 /** ---------------------------------------------------------- 00320 * @brief determine if BG96 is readable 00321 * @param none 00322 * @retval true/false 00323 */ 00324 bool BG96::readable() 00325 { 00326 return _serial.readable(); 00327 } 00328 00329 /** ---------------------------------------------------------- 00330 * @brief determine if BG96 is writable 00331 * @param none 00332 * @retval true/false 00333 */ 00334 bool BG96::writeable() 00335 { 00336 return _serial.writable(); 00337 } 00338 00339 00340 /** ---------------------------------------------------------- 00341 * @brief obtain the IP address socket is using 00342 * @param none 00343 * @retval string containing IP or NULL on failure 00344 */ 00345 const char *BG96::getIPAddress(char *ipstr) 00346 { 00347 int cs, ct; 00348 bool done=false; 00349 00350 _bg96_mutex.lock(); 00351 _parser.set_timeout(BG96_150s_TO); 00352 done = _parser.send("AT+QIACT?") && _parser.recv("+QIACT: 1, %d,%d,\"%16[^\"]\"",&cs,&ct,ipstr); 00353 _parser.set_timeout(BG96_AT_TIMEOUT); 00354 _bg96_mutex.unlock(); 00355 00356 return done? ipstr:NULL; 00357 } 00358 00359 /** ---------------------------------------------------------- 00360 * @brief return the MAC 00361 * @param none 00362 * @retval string containing the MAC or NULL on failure 00363 * MAC is created using the ICCID of the SIM 00364 */ 00365 const char *BG96::getMACAddress(char* sn) 00366 { 00367 00368 _bg96_mutex.lock(); 00369 if( _parser.send("AT+QCCID") ) { 00370 _parser.recv("+QCCID: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 00371 &sn[26], &sn[25], &sn[24],&sn[23],&sn[22], 00372 &sn[21], &sn[19], &sn[18],&sn[16],&sn[15], 00373 &sn[13], &sn[12], &sn[10],&sn[9], &sn[7], 00374 &sn[6], &sn[4], &sn[3], &sn[1], &sn[0]); 00375 sn[2] = sn[5] = sn[8] = sn[11] = sn[14] = sn[17] = ':'; 00376 sn[20] = 0x00; 00377 } 00378 _bg96_mutex.unlock(); 00379 00380 return (const char*)sn; 00381 } 00382 00383 /** ---------------------------------------------------------- 00384 * @brief determine if BG96 is connected to an APN 00385 * @param none 00386 * @retval true or false 00387 */ 00388 bool BG96::isConnected(void) 00389 { 00390 char ip[25]; 00391 return getIPAddress(ip) != NULL; 00392 } 00393 00394 /** ---------------------------------------------------------- 00395 * @brief open a BG96 socket 00396 * @param type of socket to open ('u' or 't') 00397 * @param id of BG96 socket 00398 * @param address (IP) 00399 * @param port of the socket 00400 * @retval true if successful, else false on failure 00401 */ 00402 bool BG96::open(const char type, int id, const char* addr, int port) 00403 { 00404 char* stype = (char*)"TCP"; 00405 char cmd[20]; 00406 int err=1; 00407 bool ok; 00408 00409 if( type == 'u' ) 00410 stype = (char*)"UDP"; 00411 00412 _bg96_mutex.lock(); 00413 sprintf(cmd,"+QIOPEN: %d,%%d", id); 00414 _parser.set_timeout(BG96_150s_TO); 00415 ok=_parser.send("AT+QIOPEN=%d,%d,\"%s\",\"%s\",%d,0,0\r", _contextID, id, stype, addr, port) 00416 && _parser.recv(cmd, &err) 00417 && err == 0; 00418 _parser.set_timeout(BG96_AT_TIMEOUT); 00419 _bg96_mutex.unlock(); 00420 if( ok ) 00421 while( recv(id, cmd, sizeof(cmd)) ) 00422 /* clear out any residual data in BG96 buffer */; 00423 00424 return ok; 00425 } 00426 00427 /** ---------------------------------------------------------- 00428 * @brief get last error code 00429 * @param none. 00430 * @retval returns true/false if successful and updated error string 00431 */ 00432 bool BG96::getError(char *str) 00433 { 00434 char lstr[4]; 00435 int err; 00436 memset(lstr,0x00,sizeof(lstr)); 00437 _bg96_mutex.lock(); 00438 bool done = (_parser.send("AT+QIGETERROR") 00439 && _parser.recv("+QIGETERROR: %d,%[^\\r]",&err,lstr) 00440 && _parser.recv("OK") ); 00441 _bg96_mutex.unlock(); 00442 if( done ) 00443 sprintf(str,"Error:%d",err); 00444 return done; 00445 } 00446 00447 00448 /** ---------------------------------------------------------- 00449 * @brief close the BG96 socket 00450 * @param id of BG96 socket 00451 * @retval true of close successful false on failure. <0 if error 00452 */ 00453 bool BG96::close(int id) 00454 { 00455 bool done=false; 00456 00457 _bg96_mutex.lock(); 00458 _parser.set_timeout(BG96_150s_TO); 00459 done = (_parser.send("AT+QICLOSE=%d,%d", id, BG96_CLOSE_TO) && _parser.recv("OK")); 00460 _parser.set_timeout(BG96_AT_TIMEOUT); 00461 _bg96_mutex.unlock(); 00462 return done; 00463 } 00464 00465 /** ---------------------------------------------------------- 00466 * @brief send data to the BG96 00467 * @param id of BG96 socket 00468 * @param pointer to the data to send 00469 * @param number of bytes to send 00470 * @retval true if send successfull false otherwise 00471 */ 00472 bool BG96::send(int id, const void *data, uint32_t amount) 00473 { 00474 bool done; 00475 00476 _bg96_mutex.lock(); 00477 _parser.set_timeout(BG96_TX_TIMEOUT); 00478 00479 done = !_parser.send("AT+QISEND=%d,%ld", id, amount); 00480 if( !done && _parser.recv(">") ) 00481 done = (_parser.write((char*)data, (int)amount) <= 0); 00482 00483 if( !done ) 00484 done = _parser.recv("SEND OK"); 00485 _parser.set_timeout(BG96_AT_TIMEOUT); 00486 _bg96_mutex.unlock(); 00487 00488 return done; 00489 } 00490 00491 /** ---------------------------------------------------------- 00492 * @brief check if RX data has arrived 00493 * @param id of BG96 socket 00494 * @retval true/false 00495 */ 00496 bool BG96::chkRxAvail(int id) 00497 { 00498 char cmd[20]; 00499 00500 sprintf(cmd, "+QIURC: \"recv\",%d", id); 00501 _parser.set_timeout(1); 00502 int i = _parser.recv(cmd); 00503 _parser.set_timeout(BG96_AT_TIMEOUT); 00504 return i; 00505 } 00506 00507 /** ---------------------------------------------------------- 00508 * @brief check for the amount of data available to read 00509 * @param id of BG96 socket 00510 * @retval number of bytes in RX buffer or 0 00511 */ 00512 int BG96::rxAvail(int id) 00513 { 00514 int trl, hrl, url; 00515 00516 _bg96_mutex.lock(); 00517 bool done = ( _parser.send("AT+QIRD=%d,0",id) && _parser.recv("+QIRD:%d,%d,%d",&trl, &hrl, &url) ); 00518 _bg96_mutex.unlock(); 00519 if( done ) 00520 return trl-hrl; 00521 return 0; 00522 } 00523 00524 00525 /** ---------------------------------------------------------- 00526 * @brief receive data from BG96 00527 * @param id of BG96 socket 00528 * @param pointer to location to store returned data 00529 * @param count of the number of bytes to get 00530 * @retval number of bytes returned or 0 00531 */ 00532 int32_t BG96::recv(int id, void *data, uint32_t cnt) 00533 { 00534 int rxCount, ret_cnt=0; 00535 00536 _bg96_mutex.lock(); 00537 chkRxAvail(id); 00538 00539 if( _parser.send("AT+QIRD=%d,%d",id,(int)cnt) && _parser.recv("+QIRD:%d\r\n",&rxCount) ) { 00540 if( rxCount > 0 ) { 00541 _parser.getc(); //for some reason BG96 always outputs a 0x0A before the data 00542 _parser.read((char*)data, rxCount); 00543 00544 if( !_parser.recv("OK") ) 00545 rxCount = NSAPI_ERROR_DEVICE_ERROR; 00546 } 00547 ret_cnt = rxCount; 00548 } 00549 else 00550 ret_cnt = NSAPI_ERROR_DEVICE_ERROR; 00551 _bg96_mutex.unlock(); 00552 return ret_cnt; 00553 } 00554
Generated on Tue Jul 12 2022 19:02:38 by
1.7.2