Files
esp-protocols/components/esp_modem/src/esp_modem_command_library.cpp

402 lines
13 KiB
C++
Raw Normal View History

2021-03-29 19:34:45 +02:00
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
2021-03-02 21:01:44 +01:00
//
2021-03-29 19:34:45 +02:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
2021-03-02 21:01:44 +01:00
//
2021-03-29 19:34:45 +02:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2021-03-02 21:01:44 +01:00
#include <charconv>
#include <list>
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
#include "cxx_include/esp_modem_command_library.hpp"
2021-03-04 20:19:18 +01:00
namespace esp_modem::dce_commands {
2021-03-02 21:01:44 +01:00
2021-05-17 14:59:03 +02:00
static const char *TAG = "command_lib";
2021-06-01 10:21:51 +02:00
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
uint32_t timeout_ms)
2021-03-03 20:35:08 +01:00
{
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
2021-03-28 20:22:44 +02:00
return t->command(command, [&](uint8_t *data, size_t len) {
2021-06-01 10:21:51 +02:00
std::string_view response((char *)data, len);
if (data == nullptr || len == 0 || response.empty()) {
2021-05-17 14:59:03 +02:00
return command_result::TIMEOUT;
2021-06-01 10:21:51 +02:00
}
ESP_LOGD(TAG, "Response: %.*s\n", (int)response.length(), response.data());
2021-05-17 14:59:03 +02:00
for (auto &it : pass_phrase)
2021-06-01 10:21:51 +02:00
if (response.find(it) != std::string::npos) {
return command_result::OK;
2021-06-01 10:21:51 +02:00
}
2021-05-17 14:59:03 +02:00
for (auto &it : fail_phrase)
2021-06-01 10:21:51 +02:00
if (response.find(it) != std::string::npos) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-03-03 20:35:08 +01:00
return command_result::TIMEOUT;
}, timeout_ms);
2021-03-03 20:35:08 +01:00
}
2021-03-04 20:19:18 +01:00
2021-06-01 10:21:51 +02:00
static inline command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms)
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
const auto pass = std::list<std::string_view>({pass_phrase});
const auto fail = std::list<std::string_view>({fail_phrase});
return generic_command(t, command, pass, fail, timeout_ms);
}
2021-06-01 10:21:51 +02:00
static inline command_result generic_get_string(CommandableIf *t, const std::string &command, std::string_view &output, uint32_t timeout_ms = 500)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-28 20:22:44 +02:00
return t->command(command, [&](uint8_t *data, size_t len) {
2021-03-04 20:19:18 +01:00
size_t pos = 0;
2021-06-01 10:21:51 +02:00
std::string_view response((char *)data, len);
2021-03-04 20:19:18 +01:00
while ((pos = response.find('\n')) != std::string::npos) {
std::string_view token = response.substr(0, pos);
for (auto it = token.end() - 1; it > token.begin(); it--) // strip trailing CR or LF
2021-06-01 10:21:51 +02:00
if (*it == '\r' || *it == '\n') {
token.remove_suffix(1);
2021-06-01 10:21:51 +02:00
}
2021-05-17 14:59:03 +02:00
ESP_LOGV(TAG, "Token: {%.*s}\n", static_cast<int>(token.size()), token.data());
2021-03-04 20:19:18 +01:00
if (token.find("OK") != std::string::npos) {
return command_result::OK;
} else if (token.find("ERROR") != std::string::npos) {
return command_result::FAIL;
} else if (token.size() > 2) {
output = token;
2021-03-04 20:19:18 +01:00
}
2021-06-01 10:21:51 +02:00
response = response.substr(pos + 1);
2021-03-04 20:19:18 +01:00
}
return command_result::TIMEOUT;
}, timeout_ms);
}
2021-03-03 20:35:08 +01:00
2021-06-01 10:21:51 +02:00
static inline command_result generic_get_string(CommandableIf *t, const std::string &command, std::string &output, uint32_t timeout_ms = 500)
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, command, out, timeout_ms);
2021-06-01 10:21:51 +02:00
if (ret == command_result::OK) {
output = out;
2021-06-01 10:21:51 +02:00
}
return ret;
}
2021-06-01 10:21:51 +02:00
static inline command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout = 500)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-15 09:46:28 +02:00
return generic_command(t, command, "OK", "ERROR", timeout);
2021-03-03 20:35:08 +01:00
}
2021-06-01 10:21:51 +02:00
command_result sync(CommandableIf *t)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-03 20:35:08 +01:00
return generic_command_common(t, "AT\r");
}
2021-06-01 10:21:51 +02:00
command_result store_profile(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command_common(t, "AT&W\r");
}
2021-06-01 10:21:51 +02:00
command_result power_down(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command(t, "AT+QPOWD=1\r", "POWERED DOWN", "ERROR", 1000);
}
2021-06-01 10:21:51 +02:00
command_result power_down_sim7xxx(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command_common(t, "AT+CPOF\r", 1000);
}
2021-06-01 10:21:51 +02:00
command_result power_down_sim8xx(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
}
2021-06-01 10:21:51 +02:00
command_result reset(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command(t, "AT+CRESET\r", "PB DONE", "ERROR", 60000);
}
2021-06-01 10:21:51 +02:00
command_result set_baud(CommandableIf *t, int baud)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command_common(t, "AT+IPR=" + std::to_string(baud) + "\r");
}
2021-06-01 10:21:51 +02:00
command_result hang_up(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command_common(t, "ATH\r", 90000);
}
2021-06-01 10:21:51 +02:00
command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int &bcl)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
2021-04-19 11:28:53 +02:00
auto ret = generic_get_string(t, "AT+CBC\r", out);
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
2021-04-19 11:28:53 +02:00
return ret;
2021-06-01 10:21:51 +02:00
}
constexpr std::string_view pattern = "+CBC: ";
2021-06-01 10:21:51 +02:00
if (out.find(pattern) == std::string_view::npos) {
2021-04-19 11:28:53 +02:00
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
// Parsing +CBC: <bcs>,<bcl>,<voltage>
out = out.substr(pattern.size());
int pos, value, property = 0;
while ((pos = out.find(',')) != std::string::npos) {
2021-06-01 10:21:51 +02:00
if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
switch (property++) {
2021-06-01 10:21:51 +02:00
case 0: bcs = value;
break;
case 1: bcl = value;
break;
default:
return command_result::FAIL;
}
out = out.substr(pos + 1);
}
2021-06-01 10:21:51 +02:00
if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
return command_result::OK;
}
2021-06-01 10:21:51 +02:00
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
2021-04-19 11:28:53 +02:00
auto ret = generic_get_string(t, "AT+CBC\r", out);
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
2021-04-19 11:28:53 +02:00
return ret;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
// Parsing +CBC: <voltage in Volts> V
constexpr std::string_view pattern = "+CBC: ";
constexpr int num_pos = pattern.size();
int dot_pos;
if (out.find(pattern) == std::string::npos ||
2021-06-01 10:21:51 +02:00
(dot_pos = out.find('.')) == std::string::npos) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
int volt, fraction;
2021-06-01 10:21:51 +02:00
if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
bcl = bcs = -1; // not available for these models
2021-06-01 10:21:51 +02:00
voltage = 1000 * volt + fraction;
2021-04-19 11:28:53 +02:00
return command_result::OK;
}
2021-06-01 10:21:51 +02:00
command_result set_flow_control(CommandableIf *t, int dce_flow, int dte_flow)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command_common(t, "AT+IFC=" + std::to_string(dce_flow) + ", " + std::to_string(dte_flow) + "\r");
}
2021-06-01 10:21:51 +02:00
command_result get_operator_name(CommandableIf *t, std::string &operator_name)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
2021-04-19 11:28:53 +02:00
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
2021-04-19 11:28:53 +02:00
return ret;
2021-06-01 10:21:51 +02:00
}
2021-04-19 11:28:53 +02:00
auto pos = out.find("+COPS");
auto property = 0;
while (pos != std::string::npos) {
// Looking for: +COPS: <mode>[, <format>[, <oper>]]
if (property++ == 2) { // operator name is after second comma (as a 3rd property of COPS string)
operator_name = out.substr(++pos);
return command_result::OK;
}
pos = out.find(',', ++pos);
}
return command_result::FAIL;
}
2021-06-01 10:21:51 +02:00
command_result set_echo(CommandableIf *t, bool on)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
if (on) {
2021-03-03 20:35:08 +01:00
return generic_command_common(t, "ATE1\r");
2021-06-01 10:21:51 +02:00
}
2021-03-03 20:35:08 +01:00
return generic_command_common(t, "ATE0\r");
}
2021-06-01 10:21:51 +02:00
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-03 20:35:08 +01:00
std::string pdp_command = "AT+CGDCONT=" + std::to_string(pdp.context_id) +
",\"" + pdp.protocol_type + "\",\"" + pdp.apn + "\"\r";
return generic_command_common(t, pdp_command);
}
2021-06-01 10:21:51 +02:00
command_result set_data_mode(CommandableIf *t)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-03 20:35:08 +01:00
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
}
2021-06-01 10:21:51 +02:00
command_result set_data_mode_sim8xx(CommandableIf *t)
2021-04-19 11:28:53 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-19 11:28:53 +02:00
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
}
2021-06-01 10:21:51 +02:00
command_result resume_data_mode(CommandableIf *t)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-03 20:35:08 +01:00
return generic_command(t, "ATO\r", "CONNECT", "ERROR", 5000);
}
2021-06-01 10:21:51 +02:00
command_result set_command_mode(CommandableIf *t)
2021-03-03 20:35:08 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
const auto fail = std::list<std::string_view>({"ERROR"});
return generic_command(t, "+++", pass, fail, 5000);
2021-03-03 20:35:08 +01:00
}
2021-06-01 10:21:51 +02:00
command_result get_imsi(CommandableIf *t, std::string &imsi_number)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-04 20:19:18 +01:00
return generic_get_string(t, "AT+CIMI\r", imsi_number, 5000);
}
2021-03-03 20:35:08 +01:00
2021-06-01 10:21:51 +02:00
command_result get_imei(CommandableIf *t, std::string &out)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-04 20:19:18 +01:00
return generic_get_string(t, "AT+CGSN\r", out, 5000);
}
2021-06-01 10:21:51 +02:00
command_result get_module_name(CommandableIf *t, std::string &out)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-04 20:19:18 +01:00
return generic_get_string(t, "AT+CGMM\r", out, 5000);
}
2021-06-01 10:21:51 +02:00
command_result sms_txt_mode(CommandableIf *t, bool txt = true)
2021-04-15 09:46:28 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
if (txt) {
return generic_command_common(t, "AT+CMGF=1\r"); // Text mode (default)
}
2021-04-15 09:46:28 +02:00
return generic_command_common(t, "AT+CMGF=0\r"); // PDU mode
}
2021-06-01 10:21:51 +02:00
command_result sms_character_set(CommandableIf *t)
2021-04-15 09:46:28 +02:00
{
// Sets the default GSM character set
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-15 09:46:28 +02:00
return generic_command_common(t, "AT+CSCS=\"GSM\"\r");
}
2021-06-01 10:21:51 +02:00
command_result send_sms(CommandableIf *t, const std::string &number, const std::string &message)
2021-04-15 09:46:28 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-04-15 09:46:28 +02:00
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
2021-06-01 10:21:51 +02:00
std::string_view response((char *)data, len);
ESP_LOGD(TAG, "Send SMS response %.*s", static_cast<int>(response.size()), response.data());
2021-04-15 09:46:28 +02:00
if (response.find('>') != std::string::npos) {
return command_result::OK;
}
return command_result::TIMEOUT;
}, 5000, ' ');
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
2021-04-15 09:46:28 +02:00
return ret;
2021-06-01 10:21:51 +02:00
}
return generic_command_common(t, message + "\x1A", 120000);
2021-04-15 09:46:28 +02:00
}
2021-06-01 10:21:51 +02:00
command_result set_cmux(CommandableIf *t)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-04 20:19:18 +01:00
return generic_command_common(t, "AT+CMUX=0\r");
}
2021-06-01 10:21:51 +02:00
command_result read_pin(CommandableIf *t, bool &pin_ok)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
return ret;
2021-06-01 10:21:51 +02:00
}
if (out.find("+CPIN:") == std::string::npos) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
if (out.find("SIM PIN") != std::string::npos || out.find("SIM PUK") != std::string::npos) {
pin_ok = false;
return command_result::OK;
}
if (out.find("READY") != std::string::npos) {
pin_ok = true;
return command_result::OK;
}
return command_result::FAIL; // Neither pin-ok, nor waiting for pin/puk -> mark as error
2021-03-04 20:19:18 +01:00
}
2021-06-01 10:21:51 +02:00
command_result set_pin(CommandableIf *t, const std::string &pin)
2021-03-04 20:19:18 +01:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
2021-03-04 20:19:18 +01:00
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
return generic_command_common(t, set_pin_command);
}
2021-03-02 21:01:44 +01:00
2021-06-01 10:21:51 +02:00
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
2021-03-30 08:25:16 +02:00
{
2021-06-01 10:21:51 +02:00
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CSQ\r", out);
2021-06-01 10:21:51 +02:00
if (ret != command_result::OK) {
return ret;
2021-06-01 10:21:51 +02:00
}
constexpr std::string_view pattern = "+CSQ: ";
constexpr int rssi_pos = pattern.size();
int ber_pos;
if (out.find(pattern) == std::string::npos ||
2021-06-01 10:21:51 +02:00
(ber_pos = out.find(',')) == std::string::npos) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
2021-06-01 10:21:51 +02:00
if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument) {
return command_result::FAIL;
2021-06-01 10:21:51 +02:00
}
return command_result::OK;
2021-03-30 08:25:16 +02:00
}
} // esp_modem::dce_commands