Skip to content

Cellular: Fix BG96 power on and connect #11372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(unittest-test-sources
stubs/SerialBase_stub.cpp
stubs/CellularStateMachine_stub.cpp
stubs/CellularContext_stub.cpp
stubs/ThisThread_stub.cpp
stubs/ConditionVariable_stub.cpp
stubs/Mutex_stub.cpp
)
Expand Down
4 changes: 2 additions & 2 deletions features/cellular/framework/AT/ATHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ void ATHandler::set_3gpp_error(int err, DeviceErrorType error_type)
for (size_t i = 0; i < sizeof(map_3gpp_errors) / sizeof(map_3gpp_errors[0]); i++) {
if (map_3gpp_errors[i][0] == err) {
_last_3gpp_error = map_3gpp_errors[i][1];
tr_debug("AT3GPP error code %d", get_3gpp_error());
tr_error("AT3GPP error code %d", get_3gpp_error());
break;
}
}
Expand All @@ -943,7 +943,7 @@ void ATHandler::at_error(bool error_code_expected, DeviceErrorType error_type)
set_3gpp_error(err, error_type);
_last_at_err.errCode = err;
_last_at_err.errType = error_type;
tr_error("AT error code %ld", err);
tr_warn("AT error code %ld", err);
} else {
tr_warn("ATHandler ERROR reading failed");
}
Expand Down
2 changes: 2 additions & 0 deletions features/cellular/framework/AT/AT_CellularContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ nsapi_error_t AT_CellularContext::open_data_channel()
connected, or timeout after 30 seconds*/
nsapi_error_t err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularContext::ppp_status_cb), _uname, _pwd, (nsapi_ip_stack_t)_pdp_type);
if (err) {
tr_error("nsapi_ppp_connect failed");
ppp_disconnected();
}

Expand Down Expand Up @@ -993,6 +994,7 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr)
tr_info("cellular_callback: PPP mode and NSAPI_STATUS_DISCONNECTED");
_cb_data.error = NSAPI_ERROR_NO_CONNECTION;
_is_connected = false;
ppp_disconnected();
}
}
#else
Expand Down
27 changes: 19 additions & 8 deletions features/cellular/framework/AT/AT_CellularDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/

#include "rtos/ThisThread.h"
#include "CellularUtil.h"
#include "AT_CellularDevice.h"
#include "AT_CellularInformation.h"
Expand Down Expand Up @@ -202,6 +203,7 @@ nsapi_error_t AT_CellularDevice::get_sim_state(SimState &state)
_at->flush();
nsapi_error_t error = _at->at_cmd_str("+CPIN", "?", simstr, sizeof(simstr));
ssize_t len = strlen(simstr);
device_err_t err = _at->get_last_device_error();
_at->unlock();

if (len != -1) {
Expand All @@ -213,7 +215,6 @@ nsapi_error_t AT_CellularDevice::get_sim_state(SimState &state)
state = SimStatePukNeeded;
} else {
simstr[len] = '\0';
tr_error("Unknown SIM state %s", simstr);
state = SimStateUnknown;
}
} else {
Expand All @@ -229,7 +230,11 @@ nsapi_error_t AT_CellularDevice::get_sim_state(SimState &state)
tr_error("SIM PUK required");
break;
case SimStateUnknown:
tr_warn("SIM state unknown");
if (err.errType == DeviceErrorTypeErrorCME && err.errCode == 14) {
tr_info("SIM busy");
} else {
tr_warn("SIM state unknown");
}
break;
default:
tr_info("SIM is ready");
Expand Down Expand Up @@ -443,12 +448,18 @@ nsapi_error_t AT_CellularDevice::init()
setup_at_handler();

_at->lock();
_at->flush();
_at->at_cmd_discard("E0", "");

_at->at_cmd_discard("+CMEE", "=1");

_at->at_cmd_discard("+CFUN", "=1");
for (int retry = 1; retry <= 3; retry++) {
_at->clear_error();
_at->flush();
_at->at_cmd_discard("E0", "");
_at->at_cmd_discard("+CMEE", "=1");
_at->at_cmd_discard("+CFUN", "=1");
if (_at->get_last_error() == NSAPI_ERROR_OK) {
break;
}
tr_debug("Wait 100ms to init modem");
rtos::ThisThread::sleep_for(100); // let modem have time to get ready
}

return _at->unlock_return_error();
}
Expand Down
5 changes: 3 additions & 2 deletions features/cellular/framework/AT/AT_CellularNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn)
if (!plmn) {
tr_debug("Automatic network registration");
NWRegisteringMode mode;
get_network_registering_mode(mode);

if (get_network_registering_mode(mode) != NSAPI_ERROR_OK) {
return NSAPI_ERROR_DEVICE_ERROR;
}
if (mode != NWModeAutomatic) {
return _at.at_cmd_discard("+COPS", "=0");
}
Expand Down
3 changes: 3 additions & 0 deletions features/cellular/framework/device/CellularDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ nsapi_error_t CellularDevice::shutdown()
}
CellularContext *curr = get_context_list();
while (curr) {
if (curr->is_connected()) {
curr->disconnect();
}
curr->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
curr = (CellularContext *)curr->_next;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ void CellularStateMachine::state_device_ready()
}
} else {
_status = 0;
_is_retry = true;
enter_to_state(STATE_INIT);
}
}
Expand Down
140 changes: 66 additions & 74 deletions features/cellular/framework/targets/QUECTEL/BG96/QUECTEL_BG96.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,92 +97,35 @@ void QUECTEL_BG96::set_ready_cb(Callback<void()> callback)
_at->set_urc_handler(DEVICE_READY_URC, callback);
}

nsapi_error_t QUECTEL_BG96::hard_power_on()
{
if (_pwr.is_connected()) {
tr_info("Modem power on");
ThisThread::sleep_for(250);
_pwr = !_active_high;
ThisThread::sleep_for(250); // BG96_Hardware_Design_V1.1 says 100 ms, but 250 ms seems to be more robust
_pwr = _active_high;
ThisThread::sleep_for(500);
}

return NSAPI_ERROR_OK;
}

nsapi_error_t QUECTEL_BG96::soft_power_on()
{
if (!_rst.is_connected()) {
return NSAPI_ERROR_OK;
}

tr_info("Reset modem");
_rst = !_active_high;
ThisThread::sleep_for(100);
_rst = _active_high;
ThisThread::sleep_for(150 + 460); // RESET_N timeout from BG96_Hardware_Design_V1.1
_rst = !_active_high;
ThisThread::sleep_for(500);

// wait for RDY
_at->lock();
_at->set_at_timeout(10 * 1000);
_at->resp_start();
_at->set_stop_tag("RDY");
bool rdy = _at->consume_to_stop_tag();
_at->set_stop_tag(OK);
_at->restore_at_timeout();

if (!rdy) {
// check if modem was silently powered on
_at->clear_error();
_at->set_at_timeout(100);
_at->at_cmd_discard("", ""); //Send AT
_at->restore_at_timeout();
}
return _at->unlock_return_error();
}

nsapi_error_t QUECTEL_BG96::hard_power_off()
{
if (_pwr.is_connected()) {
tr_info("Modem power off");
_pwr = _active_high;
ThisThread::sleep_for(650); // from BG96_Hardware_Design_V1.1
_pwr = !_active_high;
tr_info("QUECTEL_BG96::soft_power_on");
// check if modem was powered on already
if (wake_up()) {
return NSAPI_ERROR_OK;
}
if (!wake_up(true)) {
tr_error("Modem not responding");
soft_power_off();
return NSAPI_ERROR_DEVICE_ERROR;
}
}

return NSAPI_ERROR_OK;
}

nsapi_error_t QUECTEL_BG96::init()
nsapi_error_t QUECTEL_BG96::soft_power_off()
{
setup_at_handler();

int retry = 0;

_at->lock();
_at->flush();
_at->at_cmd_discard("E0", ""); // echo off

_at->at_cmd_discard("+CMEE", "=1"); // verbose responses

_at->cmd_start("AT+QPOWD");
_at->cmd_stop_read_resp();
if (_at->get_last_error() != NSAPI_ERROR_OK) {
return _at->unlock_return_error();
}

do {
_at->clear_error();
_at->at_cmd_discard("+CFUN", "=1"); // set full functionality
if (_at->get_last_error() == NSAPI_ERROR_OK) {
break;
tr_warn("Force modem off");
if (_pwr.is_connected()) {
press_button(_pwr, 650); // BG96_Hardware_Design_V1.1: Power off signal at least 650 ms
}
// wait some time that modem gets ready for CFUN command, and try again
retry++;
ThisThread::sleep_for(64); // experimental value
} while (retry < 3);

}
return _at->unlock_return_error();
}

Expand Down Expand Up @@ -215,3 +158,52 @@ void QUECTEL_BG96::urc_pdpdeact()
}
send_disconnect_to_context(cid);
}

void QUECTEL_BG96::press_button(DigitalOut &button, uint32_t timeout)
{
if (!button.is_connected()) {
return;
}
button = _active_high;
ThisThread::sleep_for(timeout);
button = !_active_high;
}

bool QUECTEL_BG96::wake_up(bool reset)
{
// check if modem is already ready
_at->lock();
_at->flush();
_at->set_at_timeout(30);
_at->cmd_start("AT");
_at->cmd_stop_read_resp();
nsapi_error_t err = _at->get_last_error();
_at->restore_at_timeout();
_at->unlock();
// modem is not responding, power it on
if (err != NSAPI_ERROR_OK) {
if (!reset) {
// BG96_Hardware_Design_V1.1 requires VBAT to be stable over 30 ms, that's handled above
tr_info("Power on modem");
press_button(_pwr, 250); // BG96_Hardware_Design_V1.1 requires time 100 ms, but 250 ms seems to be more robust
} else {
tr_warn("Reset modem");
press_button(_rst, 150); // BG96_Hardware_Design_V1.1 requires RESET_N timeout at least 150 ms
}
_at->lock();
// According to BG96_Hardware_Design_V1.1 USB is active after 4.2s, but it seems to take over 5s
_at->set_at_timeout(6000);
_at->resp_start();
_at->set_stop_tag("RDY");
bool rdy = _at->consume_to_stop_tag();
_at->set_stop_tag(OK);
_at->restore_at_timeout();
_at->unlock();
if (!rdy) {
return false;
}
}

// sync to check that AT is really responsive and to clear garbage
return _at->sync(500);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,16 @@ class QUECTEL_BG96 : public AT_CellularDevice {
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual AT_CellularInformation *open_information_impl(ATHandler &at);
virtual void set_ready_cb(Callback<void()> callback);
virtual nsapi_error_t hard_power_on();
virtual nsapi_error_t hard_power_off();
virtual nsapi_error_t soft_power_on();
virtual nsapi_error_t init();
virtual nsapi_error_t soft_power_off();
virtual void set_at_urcs_impl();

public:
void handle_urc(FileHandle *fh);

private:
nsapi_error_t press_power_button(uint32_t timeout);
void press_button(DigitalOut &button, uint32_t timeout);
bool wake_up(bool reset = false);
bool _active_high;
DigitalOut _pwr;
DigitalOut _rst;
Expand Down