Jim Flynn / Mbed OS aws-iot-device-sdk-mbed-c
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BG96.cpp Source File

BG96.cpp

Go to the documentation of this file.
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