mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-17 20:42:21 +02:00
esp_modem: Moved to component folder
This commit is contained in:
committed by
David Cermak
parent
61f264f97a
commit
90641c89eb
72
components/esp_modem/src/esp_modem_api.cpp
Normal file
72
components/esp_modem/src/esp_modem_api.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <cassert>
|
||||
#include "esp_log.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "uart_terminal.hpp"
|
||||
#include "vfs_termial.hpp"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
struct PdpContext;
|
||||
|
||||
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
static const char *TAG = "modem_api";
|
||||
#endif
|
||||
|
||||
std::shared_ptr<DTE> create_uart_dte(const dte_config *config) {
|
||||
TRY_CATCH_RET_NULL(
|
||||
auto term = create_uart_terminal(config);
|
||||
return std::make_shared<DTE>(config, std::move(term));
|
||||
)
|
||||
}
|
||||
|
||||
std::shared_ptr<DTE> create_vfs_dte(const dte_config *config) {
|
||||
TRY_CATCH_RET_NULL(
|
||||
auto term = create_vfs_terminal(config);
|
||||
return std::make_shared<DTE>(config, std::move(term));
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
static inline std::unique_ptr<DCE>
|
||||
create_modem_dce(dce_factory::Modem m, const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
|
||||
dce_factory::Factory f(m);
|
||||
TRY_CATCH_RET_NULL(
|
||||
return f.build_unique(config, std::move(dte), netif);
|
||||
)
|
||||
}
|
||||
|
||||
std::unique_ptr<DCE> create_SIM7600_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
|
||||
return create_modem_dce(dce_factory::Modem::SIM7600, config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
std::unique_ptr<DCE> create_SIM800_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
|
||||
return create_modem_dce(dce_factory::Modem::SIM800, config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
|
||||
return create_modem_dce(dce_factory::Modem::BG96, config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
|
||||
return create_modem_dce(dce_factory::Modem::GenericModule, config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
171
components/esp_modem/src/esp_modem_c_api.cpp
Normal file
171
components/esp_modem/src/esp_modem_c_api.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <cassert>
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "uart_terminal.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "esp_modem_c_api_types.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
#include "cstring"
|
||||
|
||||
//
|
||||
// C API definitions
|
||||
using namespace esp_modem;
|
||||
|
||||
struct esp_modem_dce_wrap // need to mimic the polymorphic dispatch as CPP uses templated dispatch
|
||||
{
|
||||
enum class modem_wrap_dte_type { UART, } dte_type;
|
||||
dce_factory::Modem modem_type;
|
||||
DCE* dce;
|
||||
};
|
||||
|
||||
static inline esp_err_t command_response_to_esp_err(command_result res)
|
||||
{
|
||||
switch (res) {
|
||||
case command_result::OK:
|
||||
return ESP_OK;
|
||||
case command_result::FAIL:
|
||||
return ESP_FAIL;
|
||||
case command_result::TIMEOUT:
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
static inline dce_factory::Modem convert_modem_enum(esp_modem_dce_device_t module)
|
||||
{
|
||||
switch (module) {
|
||||
case ESP_MODEM_DCE_SIM7600:
|
||||
return esp_modem::dce_factory::Modem::SIM7600;
|
||||
case ESP_MODEM_DCE_BG96:
|
||||
return esp_modem::dce_factory::Modem::BG96;
|
||||
case ESP_MODEM_DCE_SIM800:
|
||||
return esp_modem::dce_factory::Modem::SIM800;
|
||||
default:
|
||||
case ESP_MODEM_DCE_GENETIC:
|
||||
return esp_modem::dce_factory::Modem::GenericModule;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
|
||||
{
|
||||
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
|
||||
if (dce_wrap == nullptr)
|
||||
return nullptr;
|
||||
auto dte = create_uart_dte(dte_config);
|
||||
if (dte == nullptr) {
|
||||
delete dce_wrap;
|
||||
return nullptr;
|
||||
}
|
||||
dce_factory::Factory f(convert_modem_enum(module));
|
||||
dce_wrap->dce = f.build(dce_config, std::move(dte), netif);
|
||||
if (dce_wrap->dce == nullptr) {
|
||||
delete dce_wrap;
|
||||
return nullptr;
|
||||
}
|
||||
dce_wrap->modem_type = convert_modem_enum(module);
|
||||
dce_wrap->dte_type = esp_modem_dce_wrap::modem_wrap_dte_type::UART;
|
||||
return dce_wrap;
|
||||
}
|
||||
|
||||
extern "C" esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
|
||||
{
|
||||
return esp_modem_new_dev(ESP_MODEM_DCE_GENETIC, dte_config, dce_config, netif);
|
||||
}
|
||||
|
||||
extern "C" void esp_modem_destroy(esp_modem_dce_t * dce_wrap)
|
||||
{
|
||||
if (dce_wrap) {
|
||||
delete dce_wrap->dce;
|
||||
delete dce_wrap;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce_wrap, esp_modem_dce_mode_t mode)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
if (mode == ESP_MODEM_MODE_DATA) {
|
||||
dce_wrap->dce->set_data();
|
||||
} else if (mode == ESP_MODEM_MODE_COMMAND) {
|
||||
dce_wrap->dce->exit_data();
|
||||
} else {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t * dce_wrap, bool *pin)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t * dce_wrap, bool txt)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
return command_response_to_esp_err(dce_wrap->dce->sms_txt_mode(txt));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_send_sms(esp_modem_dce_t * dce_wrap, const char * number, const char * message)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
std::string number_str(number);
|
||||
std::string message_str(message);
|
||||
return command_response_to_esp_err(dce_wrap->dce->send_sms(number_str, message_str));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_sms_character_set(esp_modem_dce_t * dce_wrap)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
return command_response_to_esp_err(dce_wrap->dce->sms_character_set());
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t * dce_wrap, const char *pin)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
std::string pin_str(pin);
|
||||
return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t * dce_wrap, int *rssi, int *ber)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
return command_response_to_esp_err(dce_wrap->dce->get_signal_quality(*rssi, *ber));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t * dce_wrap, char *p_imsi)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
std::string imsi;
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imsi(imsi));
|
||||
if (ret == ESP_OK && !imsi.empty()) {
|
||||
strcpy(p_imsi, imsi.c_str());
|
||||
}
|
||||
return ret;
|
||||
}
|
316
components/esp_modem/src/esp_modem_cmux.cpp
Normal file
316
components/esp_modem/src/esp_modem_cmux.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <cxx_include/esp_modem_cmux.hpp>
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
/**
|
||||
* @brief Define this to defragment partially received data of CMUX payload
|
||||
* This is useful if upper layers expect the entire payload available
|
||||
* for parsing.
|
||||
*/
|
||||
#define DEFRAGMENT_CMUX_PAYLOAD
|
||||
|
||||
#define EA 0x01 /* Extension bit */
|
||||
#define CR 0x02 /* Command / Response */
|
||||
#define PF 0x10 /* Poll / Final */
|
||||
|
||||
/* Frame types */
|
||||
#define FT_RR 0x01 /* Receive Ready */
|
||||
#define FT_UI 0x03 /* Unnumbered Information */
|
||||
#define FT_RNR 0x05 /* Receive Not Ready */
|
||||
#define FT_REJ 0x09 /* Reject */
|
||||
#define FT_DM 0x0F /* Disconnected Mode */
|
||||
#define FT_SABM 0x2F /* Set Asynchronous Balanced Mode */
|
||||
#define FT_DISC 0x43 /* Disconnect */
|
||||
#define FT_UA 0x63 /* Unnumbered Acknowledgement */
|
||||
#define FT_UIH 0xEF /* Unnumbered Information with Header check */
|
||||
|
||||
/* Control channel commands */
|
||||
#define CMD_NSC 0x08 /* Non Supported Command Response */
|
||||
#define CMD_TEST 0x10 /* Test Command */
|
||||
#define CMD_PSC 0x20 /* Power Saving Control */
|
||||
#define CMD_RLS 0x28 /* Remote Line Status Command */
|
||||
#define CMD_FCOFF 0x30 /* Flow Control Off Command */
|
||||
#define CMD_PN 0x40 /* DLC parameter negotiation */
|
||||
#define CMD_RPN 0x48 /* Remote Port Negotiation Command */
|
||||
#define CMD_FCON 0x50 /* Flow Control On Command */
|
||||
#define CMD_CLD 0x60 /* Multiplexer close down */
|
||||
#define CMD_SNC 0x68 /* Service Negotiation Command */
|
||||
#define CMD_MSC 0x70 /* Modem Status Command */
|
||||
|
||||
/* Flag sequence field between messages (start of frame) */
|
||||
#define SOF_MARKER 0xF9
|
||||
|
||||
uint8_t CMux::fcs_crc(const uint8_t frame[6])
|
||||
{
|
||||
// #define FCS_GOOD_VALUE 0xCF
|
||||
uint8_t crc = 0xFF; // FCS_INIT_VALUE
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
crc ^= frame[i];
|
||||
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (crc & 0x01) {
|
||||
crc = (crc >> 1) ^ 0xe0; // FCS_POLYNOMIAL
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void CMux::send_sabm(size_t i)
|
||||
{
|
||||
uint8_t frame[6];
|
||||
frame[0] = SOF_MARKER;
|
||||
frame[1] = (i << 2) | 0x3;
|
||||
frame[2] = FT_SABM | PF;
|
||||
frame[3] = 1;
|
||||
frame[4] = 0xFF - fcs_crc(frame);
|
||||
frame[5] = SOF_MARKER;
|
||||
term->write(frame, 6);
|
||||
}
|
||||
|
||||
|
||||
void CMux::data_available(uint8_t *data, size_t len)
|
||||
{
|
||||
if (data && type == 0xFF && len > 0 && dlci > 0) {
|
||||
int virtual_term = dlci - 1;
|
||||
if (virtual_term < max_terms && read_cb[virtual_term]) {
|
||||
// Post partial data (or defragment to post on CMUX footer)
|
||||
#ifdef DEFRAGMENT_CMUX_PAYLOAD
|
||||
if (payload_start == nullptr) {
|
||||
payload_start = data;
|
||||
total_payload_size = 0;
|
||||
}
|
||||
total_payload_size += len;
|
||||
#else
|
||||
read_cb[virtual_term](data, len);
|
||||
#endif
|
||||
}
|
||||
} else if (data == nullptr && type == 0x73 && len == 0) { // notify the initial SABM command
|
||||
Scoped<Lock> l(lock);
|
||||
sabm_ack = dlci;
|
||||
} else if (data == nullptr) {
|
||||
int virtual_term = dlci - 1;
|
||||
if (virtual_term < max_terms && read_cb[virtual_term]) {
|
||||
#ifdef DEFRAGMENT_CMUX_PAYLOAD
|
||||
read_cb[virtual_term](payload_start, total_payload_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
||||
{
|
||||
if (!data) {
|
||||
#ifdef DEFRAGMENT_CMUX_PAYLOAD
|
||||
auto data_to_read = buffer_size - 128; // keep 128 (max CMUX payload) backup buffer)
|
||||
if (payload_start) {
|
||||
data = payload_start + total_payload_size;
|
||||
data_to_read = payload_len + 2;
|
||||
} else {
|
||||
data = buffer.get();
|
||||
}
|
||||
actual_len = term->read(data, data_to_read);
|
||||
#else
|
||||
data = buffer.get();
|
||||
actual_len = term->read(data, buffer_size);
|
||||
#endif
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP("CMUX Received", data, actual_len, ESP_LOG_DEBUG);
|
||||
uint8_t* frame = data;
|
||||
uint8_t* recover_ptr;
|
||||
auto available_len = actual_len;
|
||||
size_t payload_offset = 0;
|
||||
size_t footer_offset = 0;
|
||||
while (available_len > 0) {
|
||||
switch (state) {
|
||||
case cmux_state::RECOVER:
|
||||
if (frame[0] == SOF_MARKER) {
|
||||
// already init state
|
||||
state = cmux_state::INIT;
|
||||
break;
|
||||
}
|
||||
recover_ptr = static_cast<uint8_t*>(memchr(frame, SOF_MARKER, available_len));
|
||||
if (recover_ptr && available_len > recover_ptr - frame) {
|
||||
available_len -= (recover_ptr - frame);
|
||||
frame = recover_ptr;
|
||||
state = cmux_state::INIT;
|
||||
ESP_LOGI("CMUX", "Protocol recovered");
|
||||
if (available_len > 1 && frame[1] == SOF_MARKER) {
|
||||
// empty frame
|
||||
available_len -= 1;
|
||||
frame += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// marker not found, continue with recovery
|
||||
return false;
|
||||
case cmux_state::INIT:
|
||||
if (frame[0] != SOF_MARKER) {
|
||||
ESP_LOGW("CMUX", "Protocol mismatch: Missed leading SOF, recovering...");
|
||||
state = cmux_state::RECOVER;
|
||||
break;
|
||||
}
|
||||
if (available_len > 1 && frame[1] == SOF_MARKER) {
|
||||
// empty frame
|
||||
available_len -= 1;
|
||||
frame += 1;
|
||||
break;
|
||||
}
|
||||
state = cmux_state::HEADER;
|
||||
available_len--;
|
||||
frame_header_offset = 1;
|
||||
frame++;
|
||||
break;
|
||||
case cmux_state::HEADER:
|
||||
if (available_len > 0 && frame_header_offset == 1 && frame[0] == SOF_MARKER) {
|
||||
// Previously trailing SOF interpreted as heading SOF, remove it and restart HEADER
|
||||
available_len--;
|
||||
frame++;
|
||||
break;
|
||||
}
|
||||
if (available_len + frame_header_offset < 4) {
|
||||
memcpy(frame_header + frame_header_offset, frame, available_len);
|
||||
frame_header_offset += available_len;
|
||||
return false; // need read more
|
||||
}
|
||||
payload_offset = std::min(available_len, 4 - frame_header_offset);
|
||||
memcpy(frame_header + frame_header_offset, frame, payload_offset);
|
||||
frame_header_offset += payload_offset;
|
||||
dlci = frame_header[1] >> 2;
|
||||
type = frame_header[2];
|
||||
payload_len = (frame_header[3] >> 1);
|
||||
frame += payload_offset;
|
||||
available_len -= payload_offset;
|
||||
state = cmux_state::PAYLOAD;
|
||||
break;
|
||||
case cmux_state::PAYLOAD:
|
||||
ESP_LOGD("CMUX", "Payload frame: dlci:%02x type:%02x payload:%d available:%d", dlci, type, payload_len, available_len);
|
||||
if (available_len < payload_len) { // payload
|
||||
state = cmux_state::PAYLOAD;
|
||||
data_available(frame, available_len); // partial read
|
||||
payload_len -= available_len;
|
||||
return false;
|
||||
} else { // complete
|
||||
if (payload_len > 0) {
|
||||
data_available(&frame[0], payload_len); // rest read
|
||||
}
|
||||
available_len -= payload_len;
|
||||
frame += payload_len;
|
||||
state = cmux_state::FOOTER;
|
||||
payload_len = 0;
|
||||
}
|
||||
break;
|
||||
case cmux_state::FOOTER:
|
||||
if (available_len + frame_header_offset < 6) {
|
||||
memcpy(frame_header + frame_header_offset, frame, available_len);
|
||||
frame_header_offset += available_len;
|
||||
return false; // need read more
|
||||
} else {
|
||||
footer_offset = std::min(available_len, 6 - frame_header_offset);
|
||||
memcpy(frame_header + frame_header_offset, frame, footer_offset);
|
||||
if (frame_header[5] != SOF_MARKER) {
|
||||
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
|
||||
payload_start = nullptr;
|
||||
total_payload_size = 0;
|
||||
state = cmux_state::RECOVER;
|
||||
break;
|
||||
}
|
||||
frame += footer_offset;
|
||||
available_len -= footer_offset;
|
||||
state = cmux_state::INIT;
|
||||
frame_header_offset = 0;
|
||||
data_available(nullptr, 0);
|
||||
payload_start = nullptr;
|
||||
total_payload_size = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMux::init()
|
||||
{
|
||||
frame_header_offset = 0;
|
||||
state = cmux_state::INIT;
|
||||
term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
this->on_cmux(data, len);
|
||||
return false;
|
||||
});
|
||||
|
||||
sabm_ack = -1;
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
{
|
||||
int timeout = 0;
|
||||
send_sabm(i);
|
||||
while (true) {
|
||||
usleep(10'000);
|
||||
Scoped<Lock> l(lock);
|
||||
if (sabm_ack == i) {
|
||||
sabm_ack = -1;
|
||||
break;
|
||||
}
|
||||
if (timeout++ > 100)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int CMux::write(int virtual_term, uint8_t *data, size_t len)
|
||||
{
|
||||
const size_t cmux_max_len = 127;
|
||||
Scoped<Lock> l(lock);
|
||||
int i = virtual_term + 1;
|
||||
size_t need_write = len;
|
||||
while (need_write > 0) {
|
||||
size_t batch_len = need_write;
|
||||
if (batch_len > cmux_max_len)
|
||||
batch_len = cmux_max_len;
|
||||
uint8_t frame[6];
|
||||
frame[0] = SOF_MARKER;
|
||||
frame[1] = (i << 2) + 1;
|
||||
frame[2] = FT_UIH;
|
||||
frame[3] = (batch_len << 1) + 1;
|
||||
frame[4] = 0xFF - fcs_crc(frame);
|
||||
frame[5] = SOF_MARKER;
|
||||
|
||||
term->write(frame, 4);
|
||||
term->write(data, batch_len);
|
||||
term->write(frame + 4, 2);
|
||||
ESP_LOG_BUFFER_HEXDUMP("Send", frame, 4, ESP_LOG_VERBOSE);
|
||||
ESP_LOG_BUFFER_HEXDUMP("Send", data, batch_len, ESP_LOG_VERBOSE);
|
||||
ESP_LOG_BUFFER_HEXDUMP("Send", frame+4, 2, ESP_LOG_VERBOSE);
|
||||
need_write -= batch_len;
|
||||
data += batch_len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void CMux::set_read_cb(int inst, std::function<bool(uint8_t *, size_t)> f) {
|
||||
if (inst < max_terms)
|
||||
read_cb[inst] = std::move(f);
|
||||
}
|
379
components/esp_modem/src/esp_modem_command_library.cpp
Normal file
379
components/esp_modem/src/esp_modem_command_library.cpp
Normal file
@ -0,0 +1,379 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#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"
|
||||
|
||||
namespace esp_modem::dce_commands {
|
||||
|
||||
static const char *TAG = "command_lib";
|
||||
|
||||
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)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
|
||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||
std::string_view response((char*)data, len);
|
||||
if (data == nullptr || len == 0 || response.empty())
|
||||
return command_result::TIMEOUT;
|
||||
ESP_LOGD(TAG, "Response: %.*s\n", (int)response.length(), response.data());
|
||||
for (auto &it : pass_phrase)
|
||||
if (response.find(it) != std::string::npos)
|
||||
return command_result::OK;
|
||||
for (auto &it : fail_phrase)
|
||||
if (response.find(it) != std::string::npos)
|
||||
return command_result::FAIL;
|
||||
return command_result::TIMEOUT;
|
||||
}, timeout_ms);
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string_view& output, uint32_t timeout_ms = 500)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||
size_t pos = 0;
|
||||
std::string_view response((char*)data, len);
|
||||
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
|
||||
if (*it == '\r' || *it == '\n')
|
||||
token.remove_suffix(1);
|
||||
ESP_LOGV(TAG, "Token: {%.*s}\n", static_cast<int>(token.size()), token.data());
|
||||
|
||||
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;
|
||||
}
|
||||
response = response.substr(pos+1);
|
||||
}
|
||||
return command_result::TIMEOUT;
|
||||
}, timeout_ms);
|
||||
}
|
||||
|
||||
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, command, out, timeout_ms);
|
||||
if (ret == command_result::OK)
|
||||
output = out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline command_result generic_command_common(CommandableIf* t, const std::string &command, uint32_t timeout = 500)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, command, "OK", "ERROR", timeout);
|
||||
}
|
||||
|
||||
command_result sync(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT\r");
|
||||
}
|
||||
|
||||
command_result store_profile(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT&W\r");
|
||||
}
|
||||
|
||||
command_result power_down(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "AT+QPOWD=1\r", "POWERED DOWN", "ERROR", 1000);
|
||||
}
|
||||
|
||||
command_result power_down_sim7xxx(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT+CPOF\r", 1000);
|
||||
}
|
||||
|
||||
command_result power_down_sim8xx(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
|
||||
}
|
||||
|
||||
command_result reset(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "AT+CRESET\r", "PB DONE", "ERROR", 60000);
|
||||
}
|
||||
|
||||
command_result set_baud(CommandableIf* t, int baud)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT+IPR=" + std::to_string(baud) + "\r");
|
||||
}
|
||||
|
||||
command_result hang_up(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "ATH\r", 90000);
|
||||
}
|
||||
|
||||
command_result get_battery_status(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
|
||||
constexpr std::string_view pattern = "+CBC: ";
|
||||
if (out.find(pattern) == std::string_view::npos)
|
||||
return command_result::FAIL;
|
||||
// Parsing +CBC: <bcs>,<bcl>,<voltage>
|
||||
out = out.substr(pattern.size());
|
||||
int pos, value, property = 0;
|
||||
while ((pos = out.find(',') != std::string::npos)) {
|
||||
if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
switch (property++) {
|
||||
case 0: bcs = value;
|
||||
break;
|
||||
case 1: bcl = value;
|
||||
break;
|
||||
default:
|
||||
return command_result::FAIL;
|
||||
}
|
||||
out = out.substr(pos + 1);
|
||||
}
|
||||
if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
// 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 ||
|
||||
(dot_pos = out.find('.')) == std::string::npos)
|
||||
return command_result::FAIL;
|
||||
|
||||
int volt, fraction;
|
||||
if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
bcl = bcs = -1; // not available for these models
|
||||
voltage = 1000*volt + fraction;
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result set_flow_control(CommandableIf* t, int dce_flow, int dte_flow)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT+IFC=" + std::to_string(dce_flow) + ", " + std::to_string(dte_flow) + "\r");
|
||||
}
|
||||
|
||||
command_result get_operator_name(CommandableIf* t, std::string& operator_name)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
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;
|
||||
}
|
||||
|
||||
command_result set_echo(CommandableIf* t, bool on)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
if (on)
|
||||
return generic_command_common(t, "ATE1\r");
|
||||
return generic_command_common(t, "ATE0\r");
|
||||
}
|
||||
|
||||
command_result set_pdp_context(CommandableIf* t, PdpContext& pdp)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
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);
|
||||
}
|
||||
|
||||
command_result set_data_mode(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result set_data_mode_sim8xx(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result resume_data_mode(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command(t, "ATO\r", "CONNECT", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result set_command_mode(CommandableIf* t)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
command_result get_imsi(CommandableIf* t, std::string& imsi_number)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_get_string(t, "AT+CIMI\r", imsi_number, 5000);
|
||||
}
|
||||
|
||||
command_result get_imei(CommandableIf* t, std::string& out)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_get_string(t, "AT+CGSN\r", out, 5000);
|
||||
}
|
||||
|
||||
command_result get_module_name(CommandableIf* t, std::string& out)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_get_string(t, "AT+CGMM\r", out, 5000);
|
||||
}
|
||||
|
||||
command_result sms_txt_mode(CommandableIf* t, bool txt = true)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
if (txt)
|
||||
return generic_command_common(t, "AT+CMGF=1\r"); // Text mode (default)
|
||||
return generic_command_common(t, "AT+CMGF=0\r"); // PDU mode
|
||||
}
|
||||
|
||||
command_result sms_character_set(CommandableIf* t)
|
||||
{
|
||||
// Sets the default GSM character set
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT+CSCS=\"GSM\"\r");
|
||||
}
|
||||
|
||||
command_result send_sms(CommandableIf* t, const std::string& number, const std::string& message)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
|
||||
std::string_view response((char*)data, len);
|
||||
ESP_LOGD(TAG,"Send SMS response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find('>') != std::string::npos) {
|
||||
return command_result::OK;
|
||||
}
|
||||
return command_result::TIMEOUT;
|
||||
}, 5000, ' ');
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
return generic_command_common(t, message +"\x1A", 120000);
|
||||
}
|
||||
|
||||
|
||||
command_result set_cmux(CommandableIf* t)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
return generic_command_common(t, "AT+CMUX=0\r");
|
||||
}
|
||||
|
||||
command_result read_pin(CommandableIf* t, bool& pin_ok)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
if (out.find("+CPIN:") == std::string::npos)
|
||||
return command_result::FAIL;
|
||||
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
|
||||
}
|
||||
|
||||
command_result set_pin(CommandableIf* t, const std::string& pin)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
|
||||
return generic_command_common(t, set_pin_command);
|
||||
}
|
||||
|
||||
command_result get_signal_quality(CommandableIf* t, int &rssi, int &ber)
|
||||
{
|
||||
ESP_LOGV(TAG,"%s", __func__ );
|
||||
std::string_view out;
|
||||
auto ret = generic_get_string(t, "AT+CSQ\r", out);
|
||||
if (ret != command_result::OK)
|
||||
return ret;
|
||||
|
||||
constexpr std::string_view pattern = "+CSQ: ";
|
||||
constexpr int rssi_pos = pattern.size();
|
||||
int ber_pos;
|
||||
if (out.find(pattern) == std::string::npos ||
|
||||
(ber_pos = out.find(',')) == std::string::npos)
|
||||
return command_result::FAIL;
|
||||
|
||||
if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument)
|
||||
return command_result::FAIL;
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
} // esp_modem::dce_commands
|
72
components/esp_modem/src/esp_modem_dce.cpp
Normal file
72
components/esp_modem/src/esp_modem_dce.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
|
||||
bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
|
||||
{
|
||||
switch (m) {
|
||||
case modem_mode::UNDEF:
|
||||
break;
|
||||
case modem_mode::COMMAND_MODE:
|
||||
if (mode == modem_mode::COMMAND_MODE)
|
||||
return false;
|
||||
netif.stop();
|
||||
if (!device->set_mode(modem_mode::COMMAND_MODE))
|
||||
return false;
|
||||
dte->set_read_cb([&](uint8_t *data, size_t len) -> bool {
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_INFO);
|
||||
return false;
|
||||
});
|
||||
netif.wait_until_ppp_exits();
|
||||
dte->set_read_cb(nullptr);
|
||||
if (!dte->set_mode(modem_mode::COMMAND_MODE))
|
||||
return false;
|
||||
mode = m;
|
||||
return true;
|
||||
case modem_mode::DATA_MODE:
|
||||
if (mode == modem_mode::DATA_MODE)
|
||||
return false;
|
||||
if (!device->setup_data_mode())
|
||||
return false;
|
||||
if (!device->set_mode(modem_mode::DATA_MODE))
|
||||
return false;
|
||||
if (!dte->set_mode(modem_mode::DATA_MODE))
|
||||
return false;
|
||||
netif.start();
|
||||
mode = m;
|
||||
return true;
|
||||
case modem_mode::CMUX_MODE:
|
||||
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE)
|
||||
return false;
|
||||
device->set_mode(modem_mode::CMUX_MODE);
|
||||
if (!dte->set_mode(modem_mode::CMUX_MODE))
|
||||
return false;
|
||||
mode = modem_mode::COMMAND_MODE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
modem_mode DCE_Mode::get()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
} // esp_modem
|
131
components/esp_modem/src/esp_modem_dte.cpp
Normal file
131
components/esp_modem/src/esp_modem_dte.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <cstring>
|
||||
#include "esp_log.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
static const size_t dte_default_buffer_size = 1000;
|
||||
|
||||
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
|
||||
buffer_size(config->dte_buffer_size), consumed(0),
|
||||
buffer(std::make_unique<uint8_t[]>(buffer_size)),
|
||||
term(std::move(terminal)), command_term(term.get()), other_term(nullptr),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
|
||||
DTE::DTE(std::unique_ptr<Terminal> terminal):
|
||||
buffer_size(dte_default_buffer_size), consumed(0),
|
||||
buffer(std::make_unique<uint8_t[]>(buffer_size)),
|
||||
term(std::move(terminal)), command_term(term.get()), other_term(nullptr),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l(lock);
|
||||
command_result res = command_result::TIMEOUT;
|
||||
command_term->set_read_cb([&](uint8_t *data, size_t len) {
|
||||
if (!data) {
|
||||
data = buffer.get();
|
||||
len = command_term->read(data + consumed, buffer_size - consumed);
|
||||
} else {
|
||||
consumed = 0; // if the underlying terminal contains data, we cannot fragment
|
||||
}
|
||||
if (memchr(data + consumed, separator, len)) {
|
||||
res = got_line(data, consumed + len);
|
||||
if (res == command_result::OK || res == command_result::FAIL) {
|
||||
signal.set(GOT_LINE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
consumed += len;
|
||||
return false;
|
||||
});
|
||||
command_term->write((uint8_t *)command.c_str(), command.length());
|
||||
auto got_lf = signal.wait(GOT_LINE, time_ms);
|
||||
if (got_lf && res == command_result::TIMEOUT) {
|
||||
throw_if_esp_fail(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
consumed = 0;
|
||||
command_term->set_read_cb(nullptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
|
||||
{
|
||||
return command(cmd, got_line, time_ms, '\n');
|
||||
}
|
||||
|
||||
bool DTE::setup_cmux()
|
||||
{
|
||||
auto original_term = std::move(term);
|
||||
if (original_term == nullptr)
|
||||
return false;
|
||||
auto cmux_term = std::make_shared<CMux>(std::move(original_term), std::move(buffer), buffer_size);
|
||||
if (cmux_term == nullptr)
|
||||
return false;
|
||||
buffer_size = 0;
|
||||
if (!cmux_term->init())
|
||||
return false;
|
||||
term = std::make_unique<CMuxInstance>(cmux_term, 0);
|
||||
if (term == nullptr)
|
||||
return false;
|
||||
command_term = term.get(); // use command terminal as previously
|
||||
other_term = std::make_unique<CMuxInstance>(cmux_term, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DTE::set_mode(modem_mode m)
|
||||
{
|
||||
mode = m;
|
||||
if (m == modem_mode::DATA_MODE) {
|
||||
term->set_read_cb(on_data);
|
||||
if (other_term) { // if we have the other terminal, let's use it for commands
|
||||
command_term = other_term.get();
|
||||
}
|
||||
} else if (m == modem_mode::CMUX_MODE) {
|
||||
return setup_cmux();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
|
||||
{
|
||||
on_data = std::move(f);
|
||||
term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
if (!data) { // if no data available from terminal callback -> need to explicitly read some
|
||||
data = buffer.get();
|
||||
len = term->read(buffer.get(), buffer_size);
|
||||
}
|
||||
if (on_data)
|
||||
return on_data(data, len);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
int DTE::read(uint8_t **d, size_t len)
|
||||
{
|
||||
auto data_to_read = std::min(len, buffer_size);
|
||||
auto data = buffer.get();
|
||||
auto actual_len = term->read(data, data_to_read);
|
||||
*d = data;
|
||||
return actual_len;
|
||||
}
|
||||
|
||||
int DTE::write(uint8_t *data, size_t len)
|
||||
{
|
||||
return term->write(data, len);
|
||||
}
|
49
components/esp_modem/src/esp_modem_factory.cpp
Normal file
49
components/esp_modem/src/esp_modem_factory.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <functional>
|
||||
#include "cxx_include/esp_modem_types.hpp"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
template<typename T>
|
||||
std::shared_ptr<T> create_device(const std::shared_ptr<DTE>& dte, std::string &apn)
|
||||
{
|
||||
auto pdp = std::make_unique<PdpContext>(apn);
|
||||
return std::make_shared<T>(dte, std::move(pdp));
|
||||
}
|
||||
|
||||
std::shared_ptr<GenericModule> create_generic_module(const std::shared_ptr<DTE>& dte, std::string &apn)
|
||||
{
|
||||
return create_device<GenericModule>(dte, apn);
|
||||
}
|
||||
|
||||
std::shared_ptr<SIM7600> create_SIM7600_module(const std::shared_ptr<DTE>& dte, std::string &apn)
|
||||
{
|
||||
return create_device<SIM7600>(dte, apn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
|
||||
namespace esp_modem::dce_factory {
|
||||
std::unique_ptr<PdpContext> FactoryHelper::create_pdp_context(std::string &apn) {
|
||||
return std::unique_ptr<PdpContext>();
|
||||
}
|
||||
|
||||
}
|
80
components/esp_modem/src/esp_modem_modules.cpp
Normal file
80
components/esp_modem/src/esp_modem_modules.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "generate/esp_modem_command_declare.inc"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
GenericModule::GenericModule(std::shared_ptr<DTE> dte, const dce_config *config) :
|
||||
dte(std::move(dte)), pdp(std::make_unique<PdpContext>(config->apn)) {}
|
||||
|
||||
//
|
||||
// Define preprocessor's forwarding to dce_commands definitions
|
||||
//
|
||||
|
||||
// Helper macros to handle multiple arguments of declared API
|
||||
#define ARGS0
|
||||
#define ARGS1 , x
|
||||
#define ARGS2 , x , y
|
||||
#define ARGS3 , x , y , z
|
||||
#define _ARGS(x) ARGS ## x
|
||||
#define ARGS(x) _ARGS(x)
|
||||
|
||||
//
|
||||
// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
|
||||
//
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
|
||||
return_type GenericModule::name(__VA_ARGS__) { return esp_modem::dce_commands::name(dte.get() ARGS(arg_nr) ); }
|
||||
|
||||
DECLARE_ALL_COMMAND_APIS(return_type name(...) )
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
//
|
||||
// Handle specific commands for specific supported modems
|
||||
//
|
||||
command_result SIM7600::get_module_name(std::string &name) {
|
||||
name = "7600";
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result SIM7600::get_battery_status(int& voltage, int &bcs, int &bcl) {
|
||||
return dce_commands::get_battery_status_sim7xxx(dte.get(), voltage, bcs, bcl);
|
||||
}
|
||||
|
||||
command_result SIM7600::power_down() {
|
||||
return dce_commands::power_down_sim7xxx(dte.get());
|
||||
}
|
||||
|
||||
command_result SIM800::get_module_name(std::string &name) {
|
||||
name = "800L";
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result SIM800::power_down() {
|
||||
return dce_commands::power_down_sim8xx(dte.get());
|
||||
}
|
||||
|
||||
command_result SIM800::set_data_mode() {
|
||||
return dce_commands::set_data_mode_sim8xx(dte.get());
|
||||
}
|
||||
|
||||
command_result BG96::get_module_name(std::string &name) {
|
||||
name = "BG96";
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
}
|
115
components/esp_modem/src/esp_modem_netif.cpp
Normal file
115
components/esp_modem/src/esp_modem_netif.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include "cxx_include/esp_modem_netif.hpp"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_netif_ppp.h"
|
||||
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data) {
|
||||
auto *ppp = static_cast<Netif *>(arg);
|
||||
if (event_id < NETIF_PP_PHASE_OFFSET) {
|
||||
ESP_LOGI("esp_modem_netif", "PPP state changed event %d", event_id);
|
||||
// only notify the modem on state/error events, ignoring phase transitions
|
||||
ppp->signal.set(PPP_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len) {
|
||||
auto *ppp = static_cast<Netif *>(h);
|
||||
if (ppp->signal.is_any(PPP_STARTED)) {
|
||||
if (ppp->ppp_dte && ppp->ppp_dte->write((uint8_t *) buffer, len) > 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
|
||||
auto d = (ppp_netif_driver *) args;
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {};
|
||||
driver_ifconfig.transmit = Netif::esp_modem_dte_transmit;
|
||||
driver_ifconfig.handle = (void *) d->ppp;
|
||||
d->base.netif = esp_netif;
|
||||
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
|
||||
// check if PPP error events are enabled, if not, do enable the error occurred/state changed
|
||||
// to notify the modem layer when switching modes
|
||||
esp_netif_ppp_config_t ppp_config = { .ppp_phase_event_enabled = true, // assuming phase enabled, as earlier IDFs
|
||||
.ppp_error_event_enabled = false }; // don't provide cfg getters so we enable both events
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 4
|
||||
esp_netif_ppp_get_params(esp_netif, &ppp_config);
|
||||
#endif // ESP-IDF >= v4.4
|
||||
if (!ppp_config.ppp_error_event_enabled) {
|
||||
ppp_config.ppp_error_event_enabled = true;
|
||||
esp_netif_ppp_set_params(esp_netif, &ppp_config);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void Netif::receive(uint8_t *data, size_t len) {
|
||||
if (signal.is_any(PPP_STARTED)) {
|
||||
esp_netif_receive(driver.base.netif, data, len, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
|
||||
ppp_dte(std::move(e)), netif(ppp_netif) {
|
||||
driver.base.netif = ppp_netif;
|
||||
driver.ppp = this;
|
||||
driver.base.post_attach = esp_modem_post_attach;
|
||||
throw_if_esp_fail(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, (void *) this));
|
||||
throw_if_esp_fail(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, ppp_netif));
|
||||
throw_if_esp_fail(
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected, ppp_netif));
|
||||
throw_if_esp_fail(esp_netif_attach(ppp_netif, &driver));
|
||||
}
|
||||
|
||||
void Netif::start() {
|
||||
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
|
||||
receive(data, len);
|
||||
return false;
|
||||
});
|
||||
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
|
||||
signal.set(PPP_STARTED);
|
||||
}
|
||||
|
||||
void Netif::stop() {
|
||||
esp_netif_action_stop(driver.base.netif, nullptr, 0, nullptr);
|
||||
signal.clear(PPP_STARTED);
|
||||
}
|
||||
|
||||
Netif::~Netif() {
|
||||
if (signal.is_any(PPP_STARTED)) {
|
||||
esp_netif_action_stop(driver.base.netif, nullptr, 0, nullptr);
|
||||
signal.clear(PPP_STARTED);
|
||||
signal.wait(PPP_EXIT, 30000);
|
||||
}
|
||||
esp_event_handler_unregister(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed);
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected);
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected);
|
||||
}
|
||||
|
||||
void Netif::wait_until_ppp_exits() {
|
||||
signal.wait(PPP_EXIT, 30000);
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
63
components/esp_modem/src/esp_modem_netif_linux.cpp
Normal file
63
components/esp_modem/src/esp_modem_netif_linux.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <thread>
|
||||
#include "cxx_include/esp_modem_netif.hpp"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data) {
|
||||
}
|
||||
|
||||
esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
auto *this_netif = static_cast<Netif *>(h);
|
||||
this_netif->ppp_dte->write((uint8_t *) buffer, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void Netif::receive(uint8_t *data, size_t len)
|
||||
{
|
||||
esp_netif_receive(netif, data, len);
|
||||
}
|
||||
|
||||
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
|
||||
ppp_dte(std::move(e)), netif(ppp_netif) {}
|
||||
|
||||
void Netif::start()
|
||||
{
|
||||
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
|
||||
receive(data, len);
|
||||
return false;
|
||||
});
|
||||
netif->transmit = esp_modem_dte_transmit;
|
||||
netif->ctx = (void*)this;
|
||||
signal.set(PPP_STARTED);
|
||||
}
|
||||
|
||||
void Netif::stop() {}
|
||||
|
||||
Netif::~Netif() = default;
|
||||
|
||||
void Netif::wait_until_ppp_exits() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
104
components/esp_modem/src/esp_modem_primitives_freertos.cpp
Normal file
104
components/esp_modem/src/esp_modem_primitives_freertos.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "cxx_include/esp_modem_primitives.hpp"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
void Lock::unlock() {
|
||||
xSemaphoreGiveRecursive(m);
|
||||
}
|
||||
|
||||
Lock::Lock(): m(nullptr)
|
||||
{
|
||||
m = xSemaphoreCreateRecursiveMutex();
|
||||
throw_if_false(m != nullptr, "create signal event group failed");
|
||||
}
|
||||
|
||||
Lock::~Lock() {
|
||||
vSemaphoreDelete(m);
|
||||
}
|
||||
|
||||
void Lock::lock() {
|
||||
xSemaphoreTakeRecursive(m, portMAX_DELAY);
|
||||
}
|
||||
|
||||
|
||||
SignalGroup::SignalGroup(): event_group(nullptr)
|
||||
{
|
||||
event_group = xEventGroupCreate();
|
||||
throw_if_false(event_group != nullptr, "create signal event group failed");
|
||||
}
|
||||
|
||||
void SignalGroup::set(uint32_t bits)
|
||||
{
|
||||
xEventGroupSetBits(event_group, bits);
|
||||
}
|
||||
|
||||
void SignalGroup::clear(uint32_t bits)
|
||||
{
|
||||
xEventGroupClearBits(event_group, bits);
|
||||
}
|
||||
|
||||
bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdTRUE, pdTRUE, pdMS_TO_TICKS(time_ms));
|
||||
return bits & flags;
|
||||
}
|
||||
|
||||
bool SignalGroup::is_any(uint32_t flags)
|
||||
{
|
||||
return xEventGroupGetBits(event_group) & flags;
|
||||
}
|
||||
|
||||
bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdFALSE, pdFALSE, pdMS_TO_TICKS(time_ms));
|
||||
return bits & flags;
|
||||
}
|
||||
|
||||
SignalGroup::~SignalGroup()
|
||||
{
|
||||
if (event_group)
|
||||
vEventGroupDelete(event_group);
|
||||
}
|
||||
|
||||
Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function)
|
||||
:task_handle(nullptr)
|
||||
{
|
||||
BaseType_t ret = xTaskCreate(task_function, "vfs_task", stack_size, task_param, priority, &task_handle);
|
||||
throw_if_false(ret == pdTRUE, "create vfs task failed");
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
if (task_handle)
|
||||
vTaskDelete(task_handle);
|
||||
}
|
||||
|
||||
void Task::Delete()
|
||||
{
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
void Task::Relinquish()
|
||||
{
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
91
components/esp_modem/src/esp_modem_primitives_linux.cpp
Normal file
91
components/esp_modem/src/esp_modem_primitives_linux.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <unistd.h>
|
||||
#include "cxx_include/esp_modem_primitives.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
struct SignalGroupInternal {
|
||||
std::condition_variable notify;
|
||||
std::mutex m;
|
||||
uint32_t flags{ 0 };
|
||||
};
|
||||
|
||||
|
||||
SignalGroup::SignalGroup(): event_group(std::make_unique<SignalGroupInternal>())
|
||||
{
|
||||
}
|
||||
|
||||
void SignalGroup::set(uint32_t bits)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
event_group->flags |= bits;
|
||||
event_group->notify.notify_all();
|
||||
}
|
||||
|
||||
void SignalGroup::clear(uint32_t bits)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
event_group->flags &= ~bits;
|
||||
event_group->notify.notify_all();
|
||||
}
|
||||
|
||||
bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{
|
||||
if ((flags&event_group->flags) == flags) {
|
||||
event_group->flags &= ~flags;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// return ret != std::cv_status::timeout;
|
||||
// , [&]{return flags&event_group->flags; });
|
||||
}
|
||||
|
||||
bool SignalGroup::is_any(uint32_t flags)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
return flags&event_group->flags;
|
||||
}
|
||||
|
||||
bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ return flags&event_group->flags; });
|
||||
}
|
||||
|
||||
SignalGroup::~SignalGroup() = default;
|
||||
|
||||
Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function)
|
||||
{
|
||||
task_handle = std::thread(task_function, task_param);
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
task_handle.join();
|
||||
}
|
||||
|
||||
void Task::Delete() {}
|
||||
|
||||
void Task::Relinquish()
|
||||
{
|
||||
usleep(0);
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
181
components/esp_modem/src/esp_modem_term_fs.cpp
Normal file
181
components/esp_modem/src/esp_modem_term_fs.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
#include "uart_resource.hpp" // In case of VFS using UART
|
||||
|
||||
static const char *TAG = "fs_terminal";
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
class Resource {
|
||||
public:
|
||||
explicit Resource(const esp_modem_dte_config *config, int fd):
|
||||
uart(config->vfs_config.resource == ESP_MODEM_VFS_IS_EXTERN? std::nullopt : std::make_optional<uart_resource>(config, nullptr, fd))
|
||||
{}
|
||||
|
||||
std::optional<uart_resource> uart;
|
||||
};
|
||||
|
||||
struct File {
|
||||
explicit File(const char *name): fd(-1)
|
||||
{
|
||||
fd = open(name, O_RDWR);
|
||||
throw_if_false(fd >= 0, "Cannot open the fd");
|
||||
|
||||
// Set the FD to non-blocking mode
|
||||
int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK;
|
||||
fcntl(fd, F_SETFL, flags);
|
||||
}
|
||||
|
||||
~File() {
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
int fd;
|
||||
};
|
||||
|
||||
class FdTerminal : public Terminal {
|
||||
public:
|
||||
explicit FdTerminal(const esp_modem_dte_config *config);
|
||||
|
||||
~FdTerminal() override;
|
||||
|
||||
void start() override {
|
||||
signal.set(TASK_START);
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
signal.clear(TASK_START);
|
||||
}
|
||||
|
||||
int write(uint8_t *data, size_t len) override;
|
||||
|
||||
int read(uint8_t *data, size_t len) override;
|
||||
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
|
||||
on_data = std::move(f);
|
||||
signal.set(TASK_PARAMS);
|
||||
}
|
||||
|
||||
private:
|
||||
void task();
|
||||
|
||||
static const size_t TASK_INIT = SignalGroup::bit0;
|
||||
static const size_t TASK_START = SignalGroup::bit1;
|
||||
static const size_t TASK_STOP = SignalGroup::bit2;
|
||||
static const size_t TASK_PARAMS = SignalGroup::bit3;
|
||||
|
||||
File f;
|
||||
Resource resource;
|
||||
SignalGroup signal;
|
||||
Task task_handle;
|
||||
};
|
||||
|
||||
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config) {
|
||||
TRY_CATCH_RET_NULL(
|
||||
auto term = std::make_unique<FdTerminal>(config);
|
||||
term->start();
|
||||
return term;
|
||||
)
|
||||
}
|
||||
|
||||
FdTerminal::FdTerminal(const esp_modem_dte_config *config) :
|
||||
f(config->vfs_config.dev_name), resource(config, f.fd), signal(),
|
||||
task_handle(config->task_stack_size, config->task_priority, this, [](void* p){
|
||||
auto t = static_cast<FdTerminal *>(p);
|
||||
t->task();
|
||||
Task::Delete();
|
||||
})
|
||||
{}
|
||||
|
||||
void FdTerminal::task()
|
||||
{
|
||||
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
|
||||
signal.set(TASK_INIT);
|
||||
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
|
||||
if (signal.is_any(TASK_STOP)) {
|
||||
return; // exits to the static method where the task gets deleted
|
||||
}
|
||||
|
||||
while (signal.is_any(TASK_START)) {
|
||||
int s;
|
||||
fd_set rfds;
|
||||
struct timeval tv = {
|
||||
.tv_sec = 1,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(f.fd, &rfds);
|
||||
|
||||
s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv);
|
||||
if (signal.is_any(TASK_PARAMS)) {
|
||||
on_data_priv = on_data;
|
||||
signal.clear(TASK_PARAMS);
|
||||
}
|
||||
|
||||
if (s < 0) {
|
||||
break;
|
||||
} else if (s == 0) {
|
||||
// ESP_LOGV(TAG, "Select exited with timeout");
|
||||
} else {
|
||||
if (FD_ISSET(f.fd, &rfds)) {
|
||||
if (on_data_priv) {
|
||||
if (on_data_priv(nullptr, 0)) {
|
||||
on_data_priv = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Task::Relinquish();
|
||||
}
|
||||
}
|
||||
|
||||
int FdTerminal::read(uint8_t *data, size_t len)
|
||||
{
|
||||
int size = ::read(f.fd, data, len);
|
||||
if (size < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
ESP_LOGE(TAG, "Error occurred during read: %d", errno);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int FdTerminal::write(uint8_t *data, size_t len)
|
||||
{
|
||||
int size = ::write(f.fd, data, len);
|
||||
if (size < 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during read: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
FdTerminal::~FdTerminal()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
||||
|
75
components/esp_modem/src/esp_modem_term_uart.cpp
Normal file
75
components/esp_modem/src/esp_modem_term_uart.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "uart_resource.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
uart_resource::~uart_resource()
|
||||
{
|
||||
if (port >= UART_NUM_0 && port < UART_NUM_MAX) {
|
||||
uart_driver_delete(port);
|
||||
}
|
||||
}
|
||||
|
||||
uart_resource::uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue, int fd)
|
||||
:port(-1)
|
||||
{
|
||||
esp_err_t res;
|
||||
|
||||
/* Config UART */
|
||||
uart_config_t uart_config = {};
|
||||
uart_config.baud_rate = config->uart_config.baud_rate;
|
||||
uart_config.data_bits = config->uart_config.data_bits;
|
||||
uart_config.parity = config->uart_config.parity;
|
||||
uart_config.stop_bits = config->uart_config.stop_bits;
|
||||
uart_config.flow_ctrl = (config->uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS
|
||||
: UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_APB;
|
||||
|
||||
throw_if_esp_fail(uart_param_config(config->uart_config.port_num, &uart_config), "config uart parameter failed");
|
||||
|
||||
if (config->uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
res = uart_set_pin(config->uart_config.port_num, config->uart_config.tx_io_num, config->uart_config.rx_io_num,
|
||||
config->uart_config.rts_io_num, config->uart_config.cts_io_num);
|
||||
} else {
|
||||
res = uart_set_pin(config->uart_config.port_num, config->uart_config.tx_io_num, config->uart_config.rx_io_num,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
}
|
||||
throw_if_esp_fail(res, "config uart gpio failed");
|
||||
/* Set flow control threshold */
|
||||
if (config->uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
res = uart_set_hw_flow_ctrl(config->uart_config.port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
|
||||
} else if (config->uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_SW) {
|
||||
res = uart_set_sw_flow_ctrl(config->uart_config.port_num, true, 8, UART_FIFO_LEN - 8);
|
||||
}
|
||||
throw_if_esp_fail(res, "config uart flow control failed");
|
||||
|
||||
/* Install UART driver and get event queue used inside driver */
|
||||
res = uart_driver_install(config->uart_config.port_num,
|
||||
config->uart_config.rx_buffer_size, config->uart_config.tx_buffer_size,
|
||||
config->uart_config.event_queue_size, config->uart_config.event_queue_size ? event_queue : nullptr,
|
||||
0);
|
||||
throw_if_esp_fail(res, "install uart driver failed");
|
||||
throw_if_esp_fail(uart_set_rx_timeout(config->uart_config.port_num, 1), "set rx timeout failed");
|
||||
|
||||
throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed");
|
||||
|
||||
/* mark UART as initialized */
|
||||
port = config->uart_config.port_num;
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
181
components/esp_modem/src/esp_modem_uart.cpp
Normal file
181
components/esp_modem/src/esp_modem_uart.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "uart_resource.hpp"
|
||||
|
||||
static const char *TAG = "uart_terminal";
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
|
||||
struct uart_task {
|
||||
explicit uart_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) :
|
||||
task_handle(nullptr) {
|
||||
BaseType_t ret = xTaskCreate(task_function, "uart_task", stack_size, task_param, priority, &task_handle);
|
||||
throw_if_false(ret == pdTRUE, "create uart event task failed");
|
||||
}
|
||||
|
||||
~uart_task() {
|
||||
if (task_handle) vTaskDelete(task_handle);
|
||||
}
|
||||
|
||||
TaskHandle_t task_handle; /*!< UART event task handle */
|
||||
};
|
||||
|
||||
|
||||
|
||||
class UartTerminal : public Terminal {
|
||||
public:
|
||||
explicit UartTerminal(const esp_modem_dte_config *config) :
|
||||
event_queue(), uart(config, &event_queue, -1), signal(),
|
||||
task_handle(config->task_stack_size, config->task_priority, this, s_task) {}
|
||||
|
||||
~UartTerminal() override = default;
|
||||
|
||||
void start() override {
|
||||
signal.set(TASK_START);
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
signal.set(TASK_STOP);
|
||||
}
|
||||
|
||||
int write(uint8_t *data, size_t len) override;
|
||||
|
||||
int read(uint8_t *data, size_t len) override;
|
||||
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
|
||||
on_data = std::move(f);
|
||||
signal.set(TASK_PARAMS);
|
||||
}
|
||||
|
||||
private:
|
||||
static void s_task(void *task_param) {
|
||||
auto t = static_cast<UartTerminal *>(task_param);
|
||||
t->task();
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
void task();
|
||||
bool get_event(uart_event_t &event, uint32_t time_ms) {
|
||||
return xQueueReceive(event_queue, &event, pdMS_TO_TICKS(time_ms));
|
||||
}
|
||||
|
||||
void reset_events() {
|
||||
uart_flush_input(uart.port);
|
||||
xQueueReset(event_queue);
|
||||
}
|
||||
|
||||
static const size_t TASK_INIT = BIT0;
|
||||
static const size_t TASK_START = BIT1;
|
||||
static const size_t TASK_STOP = BIT2;
|
||||
static const size_t TASK_PARAMS = BIT3;
|
||||
|
||||
QueueHandle_t event_queue;
|
||||
uart_resource uart;
|
||||
SignalGroup signal;
|
||||
uart_task task_handle;
|
||||
};
|
||||
|
||||
std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *config) {
|
||||
TRY_CATCH_RET_NULL(
|
||||
auto term = std::make_unique<UartTerminal>(config);
|
||||
term->start();
|
||||
return term;
|
||||
)
|
||||
}
|
||||
|
||||
void UartTerminal::task() {
|
||||
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
|
||||
uart_event_t event;
|
||||
size_t len;
|
||||
signal.set(TASK_INIT);
|
||||
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
|
||||
if (signal.is_any(TASK_STOP)) {
|
||||
return; // exits to the static method where the task gets deleted
|
||||
}
|
||||
while (signal.is_any(TASK_START)) {
|
||||
if (get_event(event, 100)) {
|
||||
if (signal.is_any(TASK_PARAMS)) {
|
||||
on_data_priv = on_data;
|
||||
signal.clear(TASK_PARAMS);
|
||||
}
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
uart_get_buffered_data_len(uart.port, &len);
|
||||
if (len && on_data_priv) {
|
||||
if (on_data_priv(nullptr, len)) {
|
||||
on_data_priv = nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGW(TAG, "HW FIFO Overflow");
|
||||
if (on_error)
|
||||
on_error(terminal_error::BUFFER_OVERFLOW);
|
||||
reset_events();
|
||||
break;
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGW(TAG, "Ring Buffer Full");
|
||||
if (on_error)
|
||||
on_error(terminal_error::BUFFER_OVERFLOW);
|
||||
reset_events();
|
||||
break;
|
||||
case UART_BREAK:
|
||||
ESP_LOGW(TAG, "Rx Break");
|
||||
if (on_error)
|
||||
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
|
||||
break;
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGE(TAG, "Parity Error");
|
||||
if (on_error)
|
||||
on_error(terminal_error::CHECKSUM_ERROR);
|
||||
break;
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGE(TAG, "Frame Error");
|
||||
if (on_error)
|
||||
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "unknown uart event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int UartTerminal::read(uint8_t *data, size_t len) {
|
||||
size_t length = 0;
|
||||
uart_get_buffered_data_len(uart.port, &length);
|
||||
length = std::min(len, length);
|
||||
if (length > 0) {
|
||||
return uart_read_bytes(uart.port, data, length, portMAX_DELAY);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UartTerminal::write(uint8_t *data, size_t len) {
|
||||
return uart_write_bytes(uart.port, data, len);
|
||||
}
|
||||
|
||||
} // namespace esp_modem
|
||||
|
55
components/esp_modem/src/esp_modem_uart_linux.cpp
Normal file
55
components/esp_modem/src/esp_modem_uart_linux.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "esp_log.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "uart_resource.hpp"
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
constexpr const char *TAG = "uart_resource";
|
||||
|
||||
uart_resource::uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue, int fd): port(-1)
|
||||
{
|
||||
ESP_LOGD(TAG, "Creating uart resource" );
|
||||
struct termios tty = {};
|
||||
throw_if_false(tcgetattr(fd, &tty) == 0, "Failed to tcgetattr()");
|
||||
|
||||
tty.c_cflag &= ~PARENB;
|
||||
tty.c_cflag &= ~CSTOPB;
|
||||
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below
|
||||
tty.c_cflag |= CS8; // 8 bits per byte (most common)
|
||||
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
|
||||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
|
||||
tty.c_lflag &= ~ICANON;
|
||||
tty.c_lflag &= ~ECHO; // Disable echo
|
||||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
|
||||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
|
||||
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
|
||||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
|
||||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
|
||||
tty.c_cc[VTIME] = 0;
|
||||
tty.c_cc[VMIN] = 0;
|
||||
cfsetispeed(&tty, B115200);
|
||||
cfsetospeed(&tty, B115200);
|
||||
cfsetspeed(&tty, B115200);
|
||||
ioctl(fd, TCSETS, &tty);
|
||||
}
|
||||
|
||||
uart_resource::~uart_resource() = default;
|
||||
|
||||
} // namespace esp_modem
|
Reference in New Issue
Block a user