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.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
CellularStateMachine.cpp
00001 /* 00002 * Copyright (c) 2018, 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 "CellularStateMachine.h" 00019 #include "CellularDevice.h" 00020 #include "CellularLog.h" 00021 #include "Thread.h" 00022 #include "mbed_shared_queues.h" 00023 00024 #ifndef MBED_TRACE_MAX_LEVEL 00025 #define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO 00026 #endif 00027 00028 // timeout to wait for AT responses 00029 #define TIMEOUT_POWER_ON (1*1000) 00030 #define TIMEOUT_SIM_PIN (1*1000) 00031 #define TIMEOUT_NETWORK (10*1000) 00032 /** CellularStateMachine does connecting up to packet service attach, and 00033 * after that it's up to CellularContext::connect() to connect to PDN. 00034 * If CellularContext or an application does not set timeout (via `CellularDevice::set_timeout`) 00035 * then TIMEOUT_CONNECT is used also for connecting to PDN and also for socket operations. 00036 */ 00037 #define TIMEOUT_CONNECT (60*1000) 00038 #define TIMEOUT_REGISTRATION (180*1000) 00039 00040 // maximum time when retrying network register, attach and connect in seconds ( 20minutes ) 00041 #define TIMEOUT_NETWORK_MAX (20*60) 00042 00043 #define RETRY_COUNT_DEFAULT 3 00044 00045 00046 const int STM_STOPPED = -99; 00047 const int ACTIVE_PDP_CONTEXT = 0x01; 00048 const int ATTACHED_TO_NETWORK = 0x02; 00049 const int DEVICE_READY = 0x04; 00050 00051 namespace mbed { 00052 00053 CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw) : 00054 #ifdef MBED_CONF_RTOS_PRESENT 00055 _queue_thread(0), 00056 #endif 00057 _cellularDevice(device), _state(STATE_INIT), _next_state(_state), _target_state(_state), 00058 _event_status_cb(0), _network(nw), _queue(queue), _sim_pin(0), _retry_count(0), 00059 _event_timeout(-1), _event_id(-1), _plmn(0), _command_success(false), 00060 _is_retry(false), _cb_data(), _current_event(CellularDeviceReady), _status(0) 00061 { 00062 #if MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY == 0 00063 _start_time = 0; 00064 #else 00065 // so that not every device don't start at the exact same time (for example after power outage) 00066 _start_time = rand() % (MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY); 00067 #endif // MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY 00068 00069 // set initial retry values in seconds 00070 _retry_timeout_array[0] = 1; // double time on each retry in order to keep network happy 00071 _retry_timeout_array[1] = 2; 00072 _retry_timeout_array[2] = 4; 00073 _retry_timeout_array[3] = 8; 00074 _retry_timeout_array[4] = 16; 00075 _retry_timeout_array[5] = 32; 00076 _retry_timeout_array[6] = 64; 00077 _retry_timeout_array[7] = 128; // if around two minutes was not enough then let's wait much longer 00078 _retry_timeout_array[8] = 600; 00079 _retry_timeout_array[9] = TIMEOUT_NETWORK_MAX; 00080 _retry_array_length = CELLULAR_RETRY_ARRAY_SIZE; 00081 00082 _state_timeout_power_on = TIMEOUT_POWER_ON; 00083 _state_timeout_sim_pin = TIMEOUT_SIM_PIN; 00084 _state_timeout_registration = TIMEOUT_REGISTRATION; 00085 _state_timeout_network = TIMEOUT_NETWORK; 00086 _state_timeout_connect = TIMEOUT_CONNECT; 00087 } 00088 00089 CellularStateMachine::~CellularStateMachine() 00090 { 00091 tr_debug("CellularStateMachine destruct"); 00092 stop(); 00093 } 00094 00095 void CellularStateMachine::reset() 00096 { 00097 _state = STATE_INIT; 00098 _event_timeout = -1; 00099 _event_id = -1; 00100 _is_retry = false; 00101 _status = 0; 00102 _target_state = STATE_INIT; 00103 enter_to_state(STATE_INIT); 00104 } 00105 00106 void CellularStateMachine::stop() 00107 { 00108 tr_debug("CellularStateMachine stop"); 00109 #ifdef MBED_CONF_RTOS_PRESENT 00110 if (_queue_thread) { 00111 _queue_thread->terminate(); 00112 delete _queue_thread; 00113 _queue_thread = NULL; 00114 } 00115 #else 00116 _queue.chain(NULL); 00117 #endif 00118 00119 reset(); 00120 _event_id = STM_STOPPED; 00121 } 00122 00123 bool CellularStateMachine::power_on() 00124 { 00125 _cb_data.error = _cellularDevice.hard_power_on(); 00126 if (_cb_data.error != NSAPI_ERROR_OK ) { 00127 tr_warn("Hard power on failed."); 00128 return false; 00129 } 00130 return true; 00131 } 00132 00133 void CellularStateMachine::set_sim_pin(const char *sim_pin) 00134 { 00135 _sim_pin = sim_pin; 00136 } 00137 00138 void CellularStateMachine::set_plmn(const char *plmn) 00139 { 00140 _plmn = plmn; 00141 } 00142 00143 bool CellularStateMachine::open_sim() 00144 { 00145 CellularDevice::SimState state = CellularDevice::SimStateUnknown; 00146 // wait until SIM is readable 00147 // here you could add wait(secs) if you know start delay of your SIM 00148 _cb_data.error = _cellularDevice.get_sim_state(state); 00149 if (_cb_data.error != NSAPI_ERROR_OK ) { 00150 tr_info("Waiting for SIM (err while reading)..."); 00151 return false; 00152 } 00153 00154 // report current state so callback can set sim pin if needed 00155 _cb_data.status_data = state; 00156 send_event_cb(CellularSIMStatusChanged); 00157 00158 if (state == CellularDevice::SimStatePinNeeded) { 00159 if (_sim_pin) { 00160 tr_info("Entering PIN to open SIM"); 00161 _cb_data.error = _cellularDevice.set_pin(_sim_pin); 00162 if (_cb_data.error) { 00163 tr_error("Failed to set PIN: error %d", _cb_data.error); 00164 } 00165 } else { 00166 // No sim pin provided even it's needed, stop state machine 00167 tr_error("PIN required but no SIM pin provided."); 00168 _retry_count = CELLULAR_RETRY_ARRAY_SIZE; 00169 return false; 00170 } 00171 } 00172 00173 bool sim_ready = state == CellularDevice::SimStateReady; 00174 00175 if (sim_ready) { 00176 #ifdef MBED_CONF_CELLULAR_CLEAR_ON_CONNECT 00177 if (_cellularDevice.clear() != NSAPI_ERROR_OK ) { 00178 tr_warning("CellularDevice clear failed"); 00179 return false; 00180 } 00181 #endif 00182 _cb_data.error = _network.set_registration(_plmn); 00183 tr_debug("STM: set_registration: %d, plmn: %s", _cb_data.error, _plmn ? _plmn : "NULL"); 00184 if (_cb_data.error) { 00185 return false; 00186 } 00187 } 00188 00189 return sim_ready; 00190 } 00191 00192 bool CellularStateMachine::is_registered() 00193 { 00194 CellularNetwork::RegistrationStatus status; 00195 bool is_registered = false; 00196 00197 // accept only CGREG/CEREG. CREG is for circuit switch network changed. If we accept CREG attach will fail if also 00198 // CGREG/CEREG is not registered. 00199 for (int type = 0; type < CellularNetwork::C_REG; type++) { 00200 if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) { 00201 if (is_registered) { 00202 break; 00203 } 00204 } 00205 } 00206 00207 _cb_data.status_data = status; 00208 // in manual registering we are forcing registration to certain network so we don't accept active context or attached 00209 // as indication that device is registered to correct network. 00210 if (_plmn && strlen(_plmn)) { 00211 return is_registered; 00212 } 00213 return is_registered || _status; 00214 } 00215 00216 bool CellularStateMachine::get_network_registration(CellularNetwork::RegistrationType type, 00217 CellularNetwork::RegistrationStatus &status, bool &is_registered) 00218 { 00219 is_registered = false; 00220 bool is_roaming = false; 00221 CellularNetwork::registration_params_t reg_params; 00222 _cb_data.error = _network.get_registration_params(type, reg_params); 00223 00224 if (_cb_data.error != NSAPI_ERROR_OK ) { 00225 if (_cb_data.error != NSAPI_ERROR_UNSUPPORTED ) { 00226 tr_warn("Get network registration failed (type %d)!", type); 00227 } 00228 return false; 00229 } 00230 status = reg_params._status; 00231 switch (status) { 00232 case CellularNetwork::RegisteredRoaming: 00233 is_roaming = true;// @suppress("No break at end of case") 00234 // fall-through 00235 case CellularNetwork::RegisteredHomeNetwork: 00236 is_registered = true; 00237 break; 00238 case CellularNetwork::RegisteredSMSOnlyRoaming: 00239 is_roaming = true;// @suppress("No break at end of case") 00240 // fall-through 00241 case CellularNetwork::RegisteredSMSOnlyHome: 00242 tr_warn("SMS only network registration!"); 00243 break; 00244 case CellularNetwork::RegisteredCSFBNotPreferredRoaming: 00245 is_roaming = true; // @suppress("No break at end of case") 00246 // fall-through 00247 case CellularNetwork::RegisteredCSFBNotPreferredHome: 00248 tr_warn("Not preferred network registration!"); 00249 break; 00250 case CellularNetwork::AttachedEmergencyOnly: 00251 tr_warn("Emergency only network registration!"); 00252 break; 00253 case CellularNetwork::RegistrationDenied: 00254 case CellularNetwork::NotRegistered: 00255 case CellularNetwork::Unknown: 00256 case CellularNetwork::SearchingNetwork: 00257 default: 00258 break; 00259 } 00260 00261 if (is_roaming) { 00262 tr_info("Roaming network."); 00263 } 00264 00265 return true; 00266 } 00267 00268 void CellularStateMachine::report_failure(const char *msg) 00269 { 00270 tr_error("CellularStateMachine failure: %s", msg); 00271 00272 _event_id = -1; 00273 _cb_data.final_try = true; 00274 send_event_cb(_current_event); 00275 00276 tr_error("CellularStateMachine target state %s, current state %s", get_state_string(_target_state), get_state_string(_state)); 00277 } 00278 00279 const char *CellularStateMachine::get_state_string(CellularState state) const 00280 { 00281 #if MBED_CONF_MBED_TRACE_ENABLE 00282 static const char *strings[STATE_MAX_FSM_STATE] = { "Init", "Power", "Device ready", "SIM pin", "Signal quality", "Registering network", "Attaching network"}; 00283 return strings[state]; 00284 #else 00285 return NULL; 00286 #endif // #if MBED_CONF_MBED_TRACE_ENABLE 00287 } 00288 00289 void CellularStateMachine::enter_to_state(CellularState state) 00290 { 00291 _next_state = state; 00292 _retry_count = 0; 00293 _command_success = false; 00294 _cb_data.error = NSAPI_ERROR_OK ; 00295 _cb_data.status_data = -1; 00296 _cb_data.final_try = false; 00297 } 00298 00299 void CellularStateMachine::retry_state_or_fail() 00300 { 00301 if (_retry_count < _retry_array_length) { 00302 tr_debug("%s: retry %d/%d", get_state_string(_state), _retry_count, _retry_array_length); 00303 // send info to application/driver about error logic so it can implement proper error logic 00304 _cb_data.status_data = _current_event; 00305 _cb_data.data = &_retry_count; 00306 _cb_data.error = NSAPI_ERROR_OK ; 00307 send_event_cb(CellularStateRetryEvent); 00308 00309 _event_timeout = _retry_timeout_array[_retry_count]; 00310 _is_retry = true; 00311 _cb_data.error = NSAPI_ERROR_OK ; 00312 _retry_count++; 00313 } else { 00314 _cb_data.final_try = true; 00315 report_failure(get_state_string(_state)); 00316 } 00317 } 00318 00319 void CellularStateMachine::state_init() 00320 { 00321 change_timeout(_state_timeout_power_on); 00322 tr_info("Start connecting (timeout %d ms)", _state_timeout_power_on); 00323 _cb_data.error = _cellularDevice.is_ready(); 00324 _status = _cb_data.error ? 0 : DEVICE_READY; 00325 if (_cb_data.error != NSAPI_ERROR_OK ) { 00326 _event_timeout = _start_time; 00327 if (_start_time > 0) { 00328 tr_info("Startup delay %d ms", _start_time); 00329 } 00330 enter_to_state(STATE_POWER_ON); 00331 } else { 00332 enter_to_state(STATE_DEVICE_READY); 00333 } 00334 } 00335 00336 void CellularStateMachine::state_power_on() 00337 { 00338 change_timeout(_state_timeout_power_on); 00339 tr_info("Modem power ON (timeout %d ms)", _state_timeout_power_on); 00340 if (power_on()) { 00341 enter_to_state(STATE_DEVICE_READY); 00342 } else { 00343 // retry to power on device 00344 retry_state_or_fail(); 00345 } 00346 } 00347 00348 bool CellularStateMachine::device_ready() 00349 { 00350 tr_info("Modem ready"); 00351 00352 #ifdef MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY 00353 MBED_ASSERT(MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY >= CellularNetwork::RAT_GSM && 00354 MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY < CellularNetwork::RAT_UNKNOWN); 00355 nsapi_error_t err = _network.set_access_technology((CellularNetwork::RadioAccessTechnology)MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY); 00356 if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED ) { 00357 tr_warning("Failed to set access technology to %d", MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY); 00358 return false; 00359 } 00360 #endif // MBED_CONF_CELLULAR_DEBUG_AT 00361 00362 send_event_cb(CellularDeviceReady); 00363 _cellularDevice.set_ready_cb(0); 00364 00365 return true; 00366 } 00367 00368 void CellularStateMachine::state_device_ready() 00369 { 00370 change_timeout(_state_timeout_power_on); 00371 if (!(_status & DEVICE_READY)) { 00372 tr_debug("Device was not ready, calling soft_power_on()"); 00373 _cb_data.error = _cellularDevice.soft_power_on(); 00374 } 00375 if (_cb_data.error == NSAPI_ERROR_OK ) { 00376 _cb_data.error = _cellularDevice.init(); 00377 if (_cb_data.error == NSAPI_ERROR_OK ) { 00378 if (device_ready()) { 00379 _status = 0; 00380 enter_to_state(STATE_SIM_PIN); 00381 } else { 00382 tr_warning("Power cycle CellularDevice and restart connecting"); 00383 (void) _cellularDevice.soft_power_off(); 00384 (void) _cellularDevice.hard_power_off(); 00385 _status = 0; 00386 _is_retry = true; 00387 enter_to_state(STATE_INIT); 00388 } 00389 } 00390 } 00391 if (_cb_data.error != NSAPI_ERROR_OK ) { 00392 if (_retry_count == 0) { 00393 _cellularDevice.set_ready_cb(callback(this, &CellularStateMachine::device_ready_cb)); 00394 } 00395 retry_state_or_fail(); 00396 } 00397 } 00398 00399 void CellularStateMachine::state_sim_pin() 00400 { 00401 change_timeout(_state_timeout_sim_pin); 00402 tr_info("Setup SIM (timeout %d ms)", _state_timeout_sim_pin); 00403 if (open_sim()) { 00404 bool success = false; 00405 for (int type = 0; type < CellularNetwork::C_MAX; type++) { 00406 _cb_data.error = _network.set_registration_urc((CellularNetwork::RegistrationType)type, true); 00407 if (!_cb_data.error && (type == CellularNetwork::C_EREG || type == CellularNetwork::C_GREG)) { 00408 success = true; 00409 } 00410 } 00411 if (!success) { 00412 tr_error("Failed to set CEREG/CGREG URC's for registration"); 00413 retry_state_or_fail(); 00414 return; 00415 } 00416 if (_network.is_active_context()) { // check if context was already activated 00417 tr_debug("Active context found."); 00418 _status |= ACTIVE_PDP_CONTEXT; 00419 } 00420 CellularNetwork::AttachStatus status = CellularNetwork::Detached; // check if modem is already attached to a network 00421 if (_network.get_attach(status) == NSAPI_ERROR_OK && status == CellularNetwork::Attached) { 00422 _status |= ATTACHED_TO_NETWORK; 00423 tr_debug("Cellular already attached."); 00424 } 00425 00426 // if packet domain event reporting is not set it's not a stopper. We might lack some events when we are 00427 // dropped from the network. 00428 _cb_data.error = _network.set_packet_domain_event_reporting(true); 00429 if (_cb_data.error == NSAPI_STATUS_ERROR_UNSUPPORTED) { 00430 tr_warning("Packet domain event reporting not supported!"); 00431 } else if (_cb_data.error) { 00432 tr_warning("Packet domain event reporting set failed!"); 00433 } 00434 enter_to_state(STATE_SIGNAL_QUALITY); 00435 } else { 00436 retry_state_or_fail(); 00437 } 00438 } 00439 00440 void CellularStateMachine::state_signal_quality() 00441 { 00442 _cb_data.error = _network.get_signal_quality(_signal_quality.rssi, &_signal_quality.ber); 00443 00444 if (_cb_data.error != NSAPI_ERROR_OK ) { 00445 retry_state_or_fail(); 00446 } else { 00447 _cb_data.data = &_signal_quality; 00448 send_event_cb(_current_event); 00449 enter_to_state(STATE_REGISTERING_NETWORK); 00450 } 00451 } 00452 00453 void CellularStateMachine::state_registering() 00454 { 00455 change_timeout(_state_timeout_network); 00456 if (is_registered()) { 00457 if (_cb_data.status_data != CellularNetwork::RegisteredHomeNetwork && 00458 _cb_data.status_data != CellularNetwork::RegisteredRoaming && _status) { 00459 // there was already activated context or attached to network, and registration status is not registered, set to already registered. 00460 _cb_data.status_data = CellularNetwork::AlreadyRegistered; 00461 } 00462 _cb_data.error = NSAPI_ERROR_OK ; 00463 send_event_cb(_current_event); 00464 // we are already registered, go to attach 00465 enter_to_state(STATE_ATTACHING_NETWORK); 00466 } else { 00467 tr_info("Network registration (timeout %d ms)", _state_timeout_registration); 00468 change_timeout(_state_timeout_registration); 00469 if (!_command_success && !_plmn) { // don't call set_registration twice for manual registration 00470 _cb_data.error = _network.set_registration(_plmn); 00471 _command_success = (_cb_data.error == NSAPI_ERROR_OK ); 00472 } 00473 retry_state_or_fail(); 00474 } 00475 } 00476 00477 void CellularStateMachine::state_attaching() 00478 { 00479 if (_status != ATTACHED_TO_NETWORK) { 00480 change_timeout(_state_timeout_connect); 00481 tr_info("Attaching network (timeout %d ms)", _state_timeout_connect); 00482 _cb_data.error = _network.set_attach(); 00483 } 00484 if (_cb_data.error == NSAPI_ERROR_OK ) { 00485 _cb_data.status_data = CellularNetwork::Attached; 00486 send_event_cb(_current_event); 00487 } else { 00488 retry_state_or_fail(); 00489 } 00490 } 00491 00492 void CellularStateMachine::continue_from_state(CellularState state) 00493 { 00494 _mutex.lock(); 00495 tr_info("%s => %s", get_state_string((CellularStateMachine::CellularState)_state), 00496 get_state_string((CellularStateMachine::CellularState)state)); 00497 _state = state; 00498 enter_to_state(state); 00499 _event_id = _queue.call_in(0, this, &CellularStateMachine::event); 00500 if (!_event_id) { 00501 _event_id = -1; 00502 _cb_data.error = NSAPI_ERROR_NO_MEMORY ; 00503 report_failure("Failed to call queue."); 00504 stop(); 00505 } 00506 _mutex.unlock(); 00507 } 00508 00509 nsapi_error_t CellularStateMachine::run_to_state(CellularStateMachine::CellularState state) 00510 { 00511 _mutex.lock(); 00512 // call pre_event via queue so that it's in same thread and it's safe to decisions 00513 int id = _queue.call_in(0, this, &CellularStateMachine::pre_event, state); 00514 if (!id) { 00515 report_failure("Failed to call queue."); 00516 stop(); 00517 _mutex.unlock(); 00518 return NSAPI_ERROR_NO_MEMORY ; 00519 } 00520 _mutex.unlock(); 00521 return NSAPI_ERROR_OK ; 00522 } 00523 00524 void CellularStateMachine::pre_event(CellularState state) 00525 { 00526 if (_target_state < state) { 00527 // new wanted state will not be achieved with current _target_state so update it 00528 _target_state = state; 00529 } else { 00530 // wanted state is already / will be achieved, return without launching new event 00531 return; 00532 } 00533 // if _event_id is -1 it means that new event is not going to be launched so we must launch new event 00534 if (_event_id == -1) { 00535 if (!_cb_data.final_try) { 00536 // update next state so that we don't continue from previous state if state machine was paused and then started again. 00537 // but only if earlier try did not finish to failure, then we must continue from that state 00538 _state = _next_state; 00539 } 00540 enter_to_state(_next_state); 00541 _event_id = _queue.call_in(0, this, &CellularStateMachine::event); 00542 if (!_event_id) { 00543 _event_id = -1; 00544 report_failure("Failed to call queue."); 00545 stop(); 00546 } 00547 } 00548 } 00549 00550 bool CellularStateMachine::get_current_status(CellularStateMachine::CellularState ¤t_state, CellularStateMachine::CellularState &target_state) 00551 { 00552 bool is_running; 00553 _mutex.lock(); 00554 current_state = _state; 00555 target_state = _target_state; 00556 if (_event_id == -1 || _event_id == STM_STOPPED) { 00557 is_running = false; 00558 } else { 00559 is_running = true; 00560 } 00561 _mutex.unlock(); 00562 return is_running; 00563 } 00564 00565 void CellularStateMachine::event() 00566 { 00567 // Don't send Signal quality when in signal quality state or it can confuse callback functions when running retry logic 00568 if (_state > STATE_SIGNAL_QUALITY) { 00569 _cb_data.error = _network.get_signal_quality(_signal_quality.rssi, &_signal_quality.ber); 00570 _cb_data.data = &_signal_quality; 00571 00572 if (_cb_data.error == NSAPI_ERROR_OK ) { 00573 send_event_cb(CellularSignalQuality); 00574 if (_signal_quality.rssi == CellularNetwork::SignalQualityUnknown) { 00575 tr_info("RSSI unknown"); 00576 } else { 00577 tr_info("RSSI %d dBm", _signal_quality.rssi); 00578 } 00579 } 00580 } 00581 00582 _event_timeout = -1; 00583 _is_retry = false; 00584 00585 switch (_state) { 00586 case STATE_INIT: 00587 _current_event = CellularDeviceReady; 00588 state_init(); 00589 break; 00590 case STATE_POWER_ON: 00591 _current_event = CellularDeviceReady; 00592 state_power_on(); 00593 break; 00594 case STATE_DEVICE_READY: 00595 _current_event = CellularDeviceReady; 00596 state_device_ready(); 00597 break; 00598 case STATE_SIM_PIN: 00599 _current_event = CellularSIMStatusChanged; 00600 state_sim_pin(); 00601 break; 00602 case STATE_SIGNAL_QUALITY: 00603 _current_event = CellularSignalQuality; 00604 state_signal_quality(); 00605 break; 00606 case STATE_REGISTERING_NETWORK: 00607 _current_event = CellularRegistrationStatusChanged; 00608 state_registering(); 00609 break; 00610 case STATE_ATTACHING_NETWORK: 00611 _current_event = CellularAttachNetwork; 00612 state_attaching(); 00613 break; 00614 default: 00615 MBED_ASSERT(0); 00616 break; 00617 } 00618 00619 if (check_is_target_reached()) { 00620 _event_id = -1; 00621 return; 00622 } 00623 00624 if (_next_state != _state || _event_timeout >= 0) { 00625 if (_next_state != _state) { // state exit condition 00626 tr_debug("%s => %s", get_state_string((CellularStateMachine::CellularState)_state), 00627 get_state_string((CellularStateMachine::CellularState)_next_state)); 00628 } else { 00629 tr_info("Continue after %d seconds", _event_timeout); 00630 } 00631 _state = _next_state; 00632 if (_event_timeout == -1) { 00633 _event_timeout = 0; 00634 } 00635 _event_id = _queue.call_in(_event_timeout * 1000, callback(this, &CellularStateMachine::event)); 00636 if (!_event_id) { 00637 _cb_data.error = NSAPI_ERROR_NO_MEMORY ; 00638 report_failure("CellularStateMachine event failure!"); 00639 return; 00640 } 00641 } 00642 } 00643 00644 nsapi_error_t CellularStateMachine::start_dispatch() 00645 { 00646 #ifdef MBED_CONF_RTOS_PRESENT 00647 if (!_queue_thread) { 00648 _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "stm_queue"); 00649 _event_id = STM_STOPPED; 00650 } 00651 00652 if (_event_id == STM_STOPPED) { 00653 if (_queue_thread->start(callback(&_queue, &events::EventQueue::dispatch_forever)) != osOK) { 00654 report_failure("Failed to start thread."); 00655 stop(); 00656 return NSAPI_ERROR_NO_MEMORY ; 00657 } 00658 } 00659 00660 _event_id = -1; 00661 #else 00662 _queue.chain(mbed_event_queue()); 00663 #endif 00664 return NSAPI_ERROR_OK ; 00665 } 00666 00667 void CellularStateMachine::set_cellular_callback(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb) 00668 { 00669 _event_status_cb = status_cb; 00670 } 00671 00672 void CellularStateMachine::send_event_cb(cellular_connection_status_t status) 00673 { 00674 if (_event_status_cb) { 00675 _event_status_cb((nsapi_event_t)status, (intptr_t)&_cb_data); 00676 } 00677 } 00678 00679 void CellularStateMachine::change_timeout(const int &timeout) 00680 { 00681 _cb_data.status_data = _current_event; 00682 _cb_data.data = &timeout; 00683 _cb_data.error = NSAPI_ERROR_OK ; 00684 // event callback is a preferred method to communicate to CellularDevice, 00685 // for example calling CellularDevice::set_timeout would call back to this class 00686 send_event_cb(CellularDeviceTimeout); 00687 } 00688 00689 bool CellularStateMachine::check_is_target_reached() 00690 { 00691 if (((_target_state == _state || _target_state < _next_state) && _cb_data.error == NSAPI_ERROR_OK && !_is_retry) || 00692 _event_id == STM_STOPPED) { 00693 if (_target_state != _state && _target_state < _next_state) { 00694 // we are skipping the state, update _state to current state because we have reached it 00695 _state = _target_state; 00696 } 00697 _event_id = -1; 00698 return true; 00699 } 00700 return false; 00701 } 00702 00703 void CellularStateMachine::cellular_event_changed(nsapi_event_t ev, intptr_t ptr) 00704 { 00705 cell_callback_data_t *data = (cell_callback_data_t *)ptr; 00706 if ((cellular_connection_status_t)ev == CellularRegistrationStatusChanged && ( 00707 _state == STATE_REGISTERING_NETWORK || _state == STATE_SIGNAL_QUALITY)) { 00708 // expect packet data so only these states are valid 00709 CellularNetwork::registration_params_t reg_params; 00710 nsapi_error_t err = _network.get_registration_params(reg_params); 00711 00712 if (err == NSAPI_ERROR_OK && (reg_params._type == CellularNetwork::C_EREG || reg_params._type == CellularNetwork::C_GREG)) { 00713 if ((data->status_data == CellularNetwork::RegisteredHomeNetwork || 00714 data->status_data == CellularNetwork::RegisteredRoaming) && data->error == NSAPI_ERROR_OK ) { 00715 _queue.cancel(_event_id); 00716 _is_retry = false; 00717 _event_id = -1; 00718 if (!check_is_target_reached()) { 00719 continue_from_state(STATE_ATTACHING_NETWORK); 00720 } 00721 } 00722 } else { 00723 tr_debug("creg event, discard..."); 00724 } 00725 } 00726 } 00727 00728 void CellularStateMachine::device_ready_cb() 00729 { 00730 tr_debug("Device ready callback"); 00731 if (_state == STATE_DEVICE_READY && _cellularDevice.init() == NSAPI_ERROR_OK ) { 00732 tr_debug("State was STATE_DEVICE_READY and at mode ready, cancel state and move to next"); 00733 _queue.cancel(_event_id); 00734 _event_id = -1; 00735 if (device_ready()) { 00736 _is_retry = false; 00737 _status = 0; 00738 if (!check_is_target_reached()) { 00739 continue_from_state(STATE_SIM_PIN); 00740 } 00741 } else { 00742 continue_from_state(STATE_DEVICE_READY); 00743 } 00744 } 00745 } 00746 00747 void CellularStateMachine::set_retry_timeout_array(const uint16_t timeout[], int array_len) 00748 { 00749 if (!timeout || array_len <= 0) { 00750 _retry_array_length = 0; 00751 return; 00752 } 00753 _retry_array_length = array_len > CELLULAR_RETRY_ARRAY_SIZE ? CELLULAR_RETRY_ARRAY_SIZE : array_len; 00754 for (int i = 0; i < _retry_array_length; i++) { 00755 _retry_timeout_array[i] = timeout[i]; 00756 } 00757 } 00758 00759 void CellularStateMachine::get_retry_timeout_array(uint16_t *timeout, int &array_len) const 00760 { 00761 for (int i = 0; i < _retry_array_length; i++) { 00762 timeout[i] = _retry_timeout_array[i]; 00763 } 00764 array_len = _retry_array_length; 00765 } 00766 00767 void CellularStateMachine::set_timeout(int timeout) 00768 { 00769 _state_timeout_power_on = timeout; 00770 _state_timeout_sim_pin = timeout; 00771 _state_timeout_registration = timeout; 00772 _state_timeout_network = timeout; 00773 _state_timeout_connect = timeout; 00774 } 00775 00776 } // namespace 00777
Generated on Tue Jul 12 2022 13:54:05 by
