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.
CellularConnectionFSM.cpp
00001 /* 00002 * Copyright (c) 2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include "CellularConnectionFSM.h" 00019 00020 #ifdef CELLULAR_DEVICE 00021 00022 #ifndef MBED_TRACE_MAX_LEVEL 00023 #define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO 00024 #endif 00025 #include "CellularLog.h" 00026 00027 // timeout to wait for AT responses 00028 #define TIMEOUT_POWER_ON (1*1000) 00029 #define TIMEOUT_SIM_PIN (1*1000) 00030 #define TIMEOUT_NETWORK (10*1000) 00031 #define TIMEOUT_REGISTRATION (180*1000) 00032 00033 // maximum time when retrying network register, attach and connect in seconds ( 20minutes ) 00034 #define TIMEOUT_NETWORK_MAX (20*60) 00035 00036 #define RETRY_COUNT_DEFAULT 3 00037 00038 namespace mbed { 00039 00040 CellularConnectionFSM::CellularConnectionFSM() : 00041 _serial(0), _state(STATE_INIT), _next_state(_state), _status_callback(0), _network(0), _power(0), _sim(0), 00042 _queue(8 * EVENTS_EVENT_SIZE), _queue_thread(0), _retry_count(0), _state_retry_count(0), _at_queue(8 * EVENTS_EVENT_SIZE) 00043 { 00044 memset(_sim_pin, 0, sizeof(_sim_pin)); 00045 #if MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY == 0 00046 _start_time = 0; 00047 #else 00048 _start_time = rand() % (MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY); 00049 #endif // MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY 00050 00051 // set initial retry values in seconds 00052 _retry_timeout_array[0] = 1; 00053 _retry_timeout_array[1] = 2; 00054 _retry_timeout_array[2] = 4; 00055 _retry_timeout_array[3] = 16; 00056 _retry_timeout_array[4] = 32; 00057 _retry_timeout_array[5] = 60; 00058 _retry_timeout_array[6] = 120; 00059 _retry_timeout_array[7] = 360; 00060 _retry_timeout_array[8] = 600; 00061 _retry_timeout_array[9] = TIMEOUT_NETWORK_MAX; 00062 _retry_array_length = MAX_RETRY_ARRAY_SIZE; 00063 00064 _cellularDevice = new CELLULAR_DEVICE(_at_queue); 00065 } 00066 00067 CellularConnectionFSM::~CellularConnectionFSM() 00068 { 00069 stop(); 00070 } 00071 00072 nsapi_error_t CellularConnectionFSM::init() 00073 { 00074 _power = _cellularDevice->open_power(_serial); 00075 if (!_power) { 00076 stop(); 00077 return NSAPI_ERROR_NO_MEMORY ; 00078 } 00079 _network = _cellularDevice->open_network(_serial); 00080 if (!_network) { 00081 stop(); 00082 return NSAPI_ERROR_NO_MEMORY ; 00083 } 00084 00085 _sim = _cellularDevice->open_sim(_serial); 00086 if (!_sim) { 00087 stop(); 00088 return NSAPI_ERROR_NO_MEMORY ; 00089 } 00090 00091 _at_queue.chain(&_queue); 00092 00093 tr_info("init done..."); 00094 return NSAPI_ERROR_OK ; 00095 } 00096 00097 bool CellularConnectionFSM::open_power(FileHandle *fh) 00098 { 00099 if (!_power) { 00100 _power = _cellularDevice->open_power(fh); 00101 if (!_power) { 00102 return false; 00103 } 00104 } 00105 nsapi_error_t err = _power->on(); 00106 if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED ) { 00107 tr_warn("Cellular start failed. Power off/on."); 00108 err = _power->off(); 00109 if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED ) { 00110 tr_error("Cellular power down failed!"); 00111 } 00112 return false; 00113 } 00114 return true; 00115 } 00116 00117 void CellularConnectionFSM::set_sim_pin(const char * sim_pin) 00118 { 00119 strncpy(_sim_pin, sim_pin, sizeof(_sim_pin)); 00120 } 00121 00122 bool CellularConnectionFSM::open_sim() 00123 { 00124 CellularSIM::SimState state = CellularSIM::SimStateUnknown; 00125 // wait until SIM is readable 00126 // here you could add wait(secs) if you know start delay of your SIM 00127 while (_sim->get_sim_state(state) != NSAPI_ERROR_OK || state == CellularSIM::SimStateUnknown) { 00128 tr_info("Waiting for SIM (state %d)...", state); 00129 return false; 00130 } 00131 tr_info("Initial SIM state: %d", state); 00132 00133 if (strlen(_sim_pin)) { 00134 nsapi_error_t err; 00135 if (state == CellularSIM::SimStatePinNeeded) { 00136 tr_info("SIM pin required, entering pin: %s", _sim_pin); 00137 err = _sim->set_pin(_sim_pin); 00138 if (err) { 00139 tr_error("SIM pin set failed with: %d, bailing out...", err); 00140 return false; 00141 } 00142 // here you could add wait(secs) if you know delay of changing PIN on your SIM 00143 for (int i = 0; i < MAX_SIM_READY_WAITING_TIME; i++) { 00144 if (_sim->get_sim_state(state) == NSAPI_ERROR_OK && state == CellularSIM::SimStateReady) { 00145 break; 00146 } 00147 tr_info("SIM state: %d", state); 00148 return false; 00149 } 00150 } 00151 } else { 00152 tr_info("No SIM pin provided."); 00153 } 00154 00155 return state == CellularSIM::SimStateReady; 00156 } 00157 00158 void CellularConnectionFSM::device_ready() 00159 { 00160 CellularInformation *info = _cellularDevice->open_information(_serial); 00161 char device_info_buf[2048]; // may be up to 2048 according to 3GPP 00162 00163 if (info->get_manufacturer(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) { 00164 tr_info("Cellular device manufacturer: %s", device_info_buf); 00165 } 00166 if (info->get_model(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) { 00167 tr_info("Cellular device model: %s", device_info_buf); 00168 } 00169 if (info->get_revision(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) { 00170 tr_info("Cellular device revision: %s", device_info_buf); 00171 } 00172 } 00173 00174 bool CellularConnectionFSM::set_network_registration(char *plmn) 00175 { 00176 if (_network->set_registration(plmn) != NSAPI_ERROR_OK ) { 00177 tr_error("Failed to set network registration."); 00178 return false; 00179 } 00180 return true; 00181 } 00182 00183 bool CellularConnectionFSM::get_network_registration(CellularNetwork::RegistrationType type, 00184 CellularNetwork::RegistrationStatus &status, bool &is_registered) 00185 { 00186 is_registered = false; 00187 bool is_roaming = false; 00188 nsapi_error_t err = _network->get_registration_status(type, status); 00189 if (err != NSAPI_ERROR_OK ) { 00190 if (err != NSAPI_ERROR_UNSUPPORTED ) { 00191 tr_warn("Get network registration failed (type %d)!", type); 00192 } 00193 return false; 00194 } 00195 switch (status) { 00196 case CellularNetwork::RegisteredRoaming: 00197 is_roaming = true; 00198 // fall-through 00199 case CellularNetwork::RegisteredHomeNetwork: 00200 is_registered = true; 00201 break; 00202 case CellularNetwork::RegisteredSMSOnlyRoaming: 00203 is_roaming = true; 00204 // fall-through 00205 case CellularNetwork::RegisteredSMSOnlyHome: 00206 tr_warn("SMS only network registration!"); 00207 break; 00208 case CellularNetwork::RegisteredCSFBNotPreferredRoaming: 00209 is_roaming = true; 00210 // fall-through 00211 case CellularNetwork::RegisteredCSFBNotPreferredHome: 00212 tr_warn("Not preferred network registration!"); 00213 break; 00214 case CellularNetwork::AttachedEmergencyOnly: 00215 tr_warn("Emergency only network registration!"); 00216 break; 00217 case CellularNetwork::RegistrationDenied: 00218 case CellularNetwork::NotRegistered: 00219 case CellularNetwork::Unknown: 00220 case CellularNetwork::SearchingNetwork: 00221 default: 00222 break; 00223 } 00224 00225 if (is_roaming) { 00226 tr_warn("Roaming cellular network!"); 00227 } 00228 00229 return true; 00230 } 00231 00232 bool CellularConnectionFSM::get_attach_network(CellularNetwork::AttachStatus &status) 00233 { 00234 nsapi_error_t err = _network->get_attach(status); 00235 if (err != NSAPI_ERROR_OK ) { 00236 return false; 00237 } 00238 return true; 00239 } 00240 00241 bool CellularConnectionFSM::set_attach_network() 00242 { 00243 nsapi_error_t attach_err = _network->set_attach(); 00244 if (attach_err != NSAPI_ERROR_OK ) { 00245 return false; 00246 } 00247 return true; 00248 } 00249 00250 void CellularConnectionFSM::report_failure(const char* msg) 00251 { 00252 tr_error("Cellular network failed: %s", msg); 00253 if (_status_callback) { 00254 _status_callback(_state, _next_state); 00255 } 00256 } 00257 00258 nsapi_error_t CellularConnectionFSM::continue_to_state(CellularState state) 00259 { 00260 if (state < _state) { 00261 _state = state; 00262 } 00263 if (!_queue.call_in(0, callback(this, &CellularConnectionFSM::event))) { 00264 stop(); 00265 return NSAPI_ERROR_NO_MEMORY ; 00266 } 00267 00268 return NSAPI_ERROR_OK ; 00269 } 00270 00271 void CellularConnectionFSM::event() 00272 { 00273 nsapi_error_t err; 00274 int event_timeout = -1; 00275 00276 switch (_state) { 00277 case STATE_INIT: 00278 event_timeout = _start_time; 00279 tr_info("INIT state, waiting %d ms before POWER state)", _start_time); 00280 _next_state = STATE_POWER_ON; 00281 break; 00282 case STATE_POWER_ON: 00283 _cellularDevice->set_timeout(TIMEOUT_POWER_ON); 00284 tr_info("Cellular power ON (timeout %d ms)", TIMEOUT_POWER_ON); 00285 if (open_power(_serial)) { 00286 _next_state = STATE_DEVICE_READY; 00287 _retry_count = 0; 00288 } else { 00289 if (++_retry_count <= RETRY_COUNT_DEFAULT) { 00290 tr_warn("Power ON retry %d", _retry_count); 00291 event_timeout = 3 * 1000; 00292 } else { 00293 report_failure("Power"); 00294 return; 00295 } 00296 } 00297 break; 00298 case STATE_DEVICE_READY: 00299 _cellularDevice->set_timeout(TIMEOUT_POWER_ON); 00300 if (_power->set_at_mode() == NSAPI_ERROR_OK ) { 00301 tr_info("Cellular device ready"); 00302 _next_state = STATE_SIM_PIN; 00303 _retry_count = 0; 00304 device_ready(); 00305 } else { 00306 tr_info("Waiting for cellular device (retry %d/%d, timeout %d ms)", _retry_count, RETRY_COUNT_DEFAULT, 00307 TIMEOUT_POWER_ON); 00308 if (_retry_count++ <= RETRY_COUNT_DEFAULT) { 00309 event_timeout = 3 * 1000; 00310 } else { 00311 report_failure("Power"); 00312 return; 00313 } 00314 } 00315 break; 00316 case STATE_SIM_PIN: 00317 _cellularDevice->set_timeout(TIMEOUT_SIM_PIN); 00318 tr_info("Start cellular (timeout %d ms)", TIMEOUT_SIM_PIN); 00319 if (open_sim()) { 00320 _next_state = STATE_REGISTERING_NETWORK; 00321 _retry_count = 0; 00322 _state_retry_count = 0; 00323 tr_info("Check for network registration"); 00324 } else { 00325 if (_retry_count++ <= RETRY_COUNT_DEFAULT) { 00326 tr_warn("Waiting for SIM %d/%d", _retry_count, RETRY_COUNT_DEFAULT); 00327 event_timeout = 3 * 1000; 00328 } else { 00329 report_failure("Entering SIM PIN"); 00330 return; 00331 } 00332 } 00333 break; 00334 case STATE_REGISTERING_NETWORK: 00335 _cellularDevice->set_timeout(TIMEOUT_NETWORK); 00336 CellularNetwork::RegistrationStatus status; 00337 bool is_registered; 00338 _next_state = STATE_REGISTER_NETWORK; 00339 for (int type = 0; type < CellularNetwork::C_MAX; type++) { 00340 if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) { 00341 tr_debug("get_network_registration: type=%d, status=%d", type, status); 00342 if (is_registered) { 00343 tr_info("Registered to cellular network (type %d, status %d)", type, status); 00344 _next_state = STATE_ATTACHING_NETWORK; 00345 _retry_count = 0; 00346 _state_retry_count = 0; 00347 event_timeout = 0; 00348 tr_info("Check cellular network attach state"); 00349 break; 00350 } else { 00351 if (_retry_count < 180) { 00352 event_timeout = 1000; 00353 _next_state = STATE_REGISTERING_NETWORK; 00354 tr_info("Waiting for registration %d/180 (type %d, status %d)", _retry_count, type, status); 00355 } else { 00356 tr_info("Start cellular registration"); 00357 _next_state = STATE_REGISTER_NETWORK; 00358 _retry_count = 0; 00359 break; 00360 } 00361 } 00362 } 00363 } 00364 00365 if (_next_state == STATE_REGISTERING_NETWORK) { 00366 _retry_count++; 00367 } 00368 break; 00369 case STATE_REGISTER_NETWORK: 00370 _cellularDevice->set_timeout(TIMEOUT_REGISTRATION); 00371 tr_info("Register to cellular network (timeout %d ms)", TIMEOUT_REGISTRATION); 00372 if (set_network_registration()) { 00373 _next_state = STATE_REGISTERING_NETWORK; 00374 _retry_count = 0; 00375 if (_state_retry_count > RETRY_COUNT_DEFAULT) { 00376 report_failure("Registration retry"); 00377 return; 00378 } 00379 _state_retry_count++; 00380 } else { 00381 if (_retry_count < _retry_array_length) { 00382 event_timeout = _retry_timeout_array[_retry_count] * 1000; 00383 _retry_count++; 00384 } else { 00385 report_failure("Registration"); 00386 return; 00387 } 00388 } 00389 break; 00390 case STATE_ATTACHING_NETWORK: 00391 _cellularDevice->set_timeout(TIMEOUT_NETWORK); 00392 CellularNetwork::AttachStatus attach_status; 00393 if (get_attach_network(attach_status)) { 00394 if (attach_status == CellularNetwork::Attached) { 00395 _next_state = STATE_CONNECT_NETWORK; 00396 _retry_count = 0; 00397 } else { 00398 _next_state = STATE_ATTACH_NETWORK; 00399 _retry_count = 0; 00400 } 00401 } else { 00402 if (_retry_count++ <= RETRY_COUNT_DEFAULT) { 00403 event_timeout = 1 * 1000; 00404 } else { 00405 report_failure("Attaching"); 00406 return; 00407 } 00408 } 00409 break; 00410 case STATE_ATTACH_NETWORK: 00411 _cellularDevice->set_timeout(TIMEOUT_NETWORK); 00412 tr_info("Attach to cellular network (timeout %d ms)", TIMEOUT_NETWORK); 00413 if (set_attach_network()) { 00414 _next_state = STATE_ATTACHING_NETWORK; 00415 _retry_count = 0; 00416 if (_state_retry_count >= RETRY_COUNT_DEFAULT) { 00417 report_failure("Attach retry"); 00418 return; 00419 } 00420 _state_retry_count++; 00421 tr_info("Cellular network attaching"); 00422 } else { 00423 if (_retry_count < _retry_array_length) { 00424 event_timeout = _retry_timeout_array[_retry_count] * 1000; 00425 _retry_count++; 00426 } else { 00427 report_failure("Attach"); 00428 return; 00429 } 00430 } 00431 break; 00432 case STATE_CONNECT_NETWORK: 00433 _cellularDevice->set_timeout(TIMEOUT_NETWORK); 00434 tr_info("Connect to cellular network (timeout %d ms)", TIMEOUT_NETWORK); 00435 err = _network->connect(); 00436 if (!err) { 00437 _next_state = STATE_CONNECTED; 00438 } else { 00439 if (_retry_count < _retry_array_length) { 00440 event_timeout = _retry_timeout_array[_retry_count] * 1000; 00441 _retry_count++; 00442 } else { 00443 report_failure("Network Connect"); 00444 return; 00445 } 00446 } 00447 break; 00448 case STATE_CONNECTED: 00449 _cellularDevice->set_timeout(TIMEOUT_NETWORK); 00450 tr_debug("Cellular ready! (timeout %d ms)", TIMEOUT_NETWORK); 00451 if (_status_callback) { 00452 if (!_status_callback(_state, _next_state)) { 00453 return; 00454 } 00455 } 00456 break; 00457 default: 00458 MBED_ASSERT(0); 00459 break; 00460 } 00461 00462 if (_next_state != _state || event_timeout >= 0) { 00463 if (_next_state != _state) { // state exit condition 00464 tr_info("Cellular state from %d to %d", _state, _next_state); 00465 if (_status_callback) { 00466 if (!_status_callback(_state, _next_state)) { 00467 return; 00468 } 00469 } 00470 } else { 00471 if (event_timeout == 0) { 00472 static int retry_count = 0; 00473 if (++retry_count <= 3) { 00474 tr_info("Cellular event retry %d", retry_count); 00475 } else { 00476 report_failure("Cellular connection failed!"); 00477 return; 00478 } 00479 } else { 00480 tr_info("Cellular event in %d milliseconds", event_timeout); 00481 } 00482 } 00483 _state = _next_state; 00484 if (event_timeout == -1) { 00485 event_timeout = 0; 00486 } 00487 if (!_queue.call_in(event_timeout, callback(this, &CellularConnectionFSM::event))) { 00488 report_failure("Cellular event failure!"); 00489 return; 00490 } 00491 } 00492 } 00493 00494 nsapi_error_t CellularConnectionFSM::start_dispatch() 00495 { 00496 tr_info("CellularConnectionUtil::start"); 00497 tr_info("Create cellular thread"); 00498 00499 MBED_ASSERT(!_queue_thread); 00500 00501 _queue_thread = new rtos::Thread; 00502 if (!_queue_thread) { 00503 stop(); 00504 return NSAPI_ERROR_NO_MEMORY ; 00505 } 00506 if (_queue_thread->start(callback(&_queue, &events::EventQueue::dispatch_forever)) != osOK) { 00507 stop(); 00508 return NSAPI_ERROR_NO_MEMORY ; 00509 } 00510 00511 tr_info("CellularConnectionUtil::started"); 00512 return NSAPI_ERROR_OK ; 00513 } 00514 00515 void CellularConnectionFSM::stop() 00516 { 00517 tr_info("CellularConnectionUtil::stop"); 00518 _cellularDevice->close_power(); 00519 _cellularDevice->close_network(); 00520 if (_queue_thread) { 00521 _queue_thread->terminate(); 00522 _queue_thread = NULL; 00523 } 00524 } 00525 00526 void CellularConnectionFSM::set_serial(UARTSerial *serial) 00527 { 00528 _serial = serial; 00529 } 00530 00531 void CellularConnectionFSM::set_callback(mbed::Callback<bool(int, int)> status_callback) 00532 { 00533 _status_callback = status_callback; 00534 } 00535 00536 events::EventQueue *CellularConnectionFSM::get_queue() 00537 { 00538 return &_queue; 00539 } 00540 00541 CellularNetwork* CellularConnectionFSM::get_network() 00542 { 00543 return _network; 00544 } 00545 00546 CellularDevice* CellularConnectionFSM::get_device() 00547 { 00548 return _cellularDevice; 00549 } 00550 00551 CellularSIM* CellularConnectionFSM::get_sim() 00552 { 00553 return _sim; 00554 } 00555 00556 NetworkStack *CellularConnectionFSM::get_stack() 00557 { 00558 return _cellularDevice->get_stack(); 00559 } 00560 00561 void CellularConnectionFSM::set_retry_timeout_array(uint16_t timeout[], int array_len) 00562 { 00563 _retry_array_length = array_len > MAX_RETRY_ARRAY_SIZE ? MAX_RETRY_ARRAY_SIZE : array_len; 00564 00565 for (int i = 0; i < _retry_array_length; i++) { 00566 _retry_timeout_array[i] = timeout[i]; 00567 } 00568 } 00569 00570 } // namespace 00571 00572 #endif // CELLULAR_DEVICE
Generated on Tue Jul 12 2022 13:29:35 by
