mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-06-25 17:31:33 +02:00
fix(modem): Support for custom modules with C-API
MAJOR CHANGE: Added support for implementing user defined modules in standard C-API
This commit is contained in:
@ -42,6 +42,9 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
|
||||
if(CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE)
|
||||
idf_component_optional_requires(PUBLIC main)
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
|
||||
|
@ -45,4 +45,22 @@ menu "esp-modem"
|
||||
to make the protocol more robust on noisy environments or when underlying
|
||||
transport gets corrupted often (for example by Rx buffer overflows)
|
||||
|
||||
config ESP_MODEM_ADD_CUSTOM_MODULE
|
||||
bool "Add support for custom module in C-API"
|
||||
default n
|
||||
help
|
||||
If enabled, we adapt the C-API to create a DCE from a user defined class
|
||||
|
||||
config ESP_MODEM_CUSTOM_MODULE_HEADER
|
||||
string "Header file name which defines custom DCE creation"
|
||||
depends on ESP_MODEM_ADD_CUSTOM_MODULE
|
||||
default "custom_module.hpp"
|
||||
help
|
||||
Name of the header file in the main component which implements esp_modem_create_custom_dce()
|
||||
called from C-API for creating esp_modem_dce object.
|
||||
This header provides definition of the custom module with some additional and/or updated commands
|
||||
and API. It also defines creation of DCE based on this custom module, typically calling:
|
||||
dce_factory::Factory::create_unique_dce_from<CustomModule, DCE*>(dce_config, std::move(dte), netif)
|
||||
Please refer to the pppos_client example for more details.
|
||||
|
||||
endmenu
|
||||
|
@ -48,6 +48,13 @@ menu "Example Configuration"
|
||||
bool "A7670"
|
||||
help
|
||||
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
|
||||
|
||||
config EXAMPLE_MODEM_DEVICE_CUSTOM
|
||||
select ESP_MODEM_ADD_CUSTOM_MODULE
|
||||
bool "Custom device"
|
||||
help
|
||||
This demonstrates use of a custom device in C-API.
|
||||
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MODEM_PPP_APN
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
|
||||
/**
|
||||
* @brief Definition of a custom module based on some already defined module
|
||||
* Here we use GenericModule, but you can use any kind of device
|
||||
* that is closer to your CustomModule.
|
||||
*/
|
||||
class SIM7600_WITH_TIME: public GenericModule {
|
||||
/**
|
||||
* @brief Need to reuse the constructors of our ancestor
|
||||
*/
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
/**
|
||||
* @brief New command that is not defined in the GenericModule
|
||||
*/
|
||||
command_result get_time(std::string &time)
|
||||
{
|
||||
return esp_modem::dce_commands::generic_get_string(dte.get(), "AT+CCLK?\r", time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This command is already defined in the GenericModule
|
||||
*
|
||||
* Here we just modify get_signal_quality() to return zeroes
|
||||
* for demonstration purpose only, since it's called within this example
|
||||
*/
|
||||
command_result get_signal_quality(int &rssi, int &ber) override
|
||||
{
|
||||
rssi = ber = 0;
|
||||
return esp_modem::command_result::OK;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief esp_modem_create_custom_dce() needs to be defined, as it is called in C-API wrapper when creating esp_modem_dce
|
||||
*
|
||||
* This uses public factory function for creating a common DCE with our CustomModule. Creating raw DCE pointer is only needed
|
||||
* for the C-API wrapper; C++API users would create DCE (any kind of smart pointer) directly with
|
||||
* Factory::create_unique_dce_from<CustomModule>(dce_config, std::move(dte), netif);
|
||||
*/
|
||||
DCE *esp_modem_create_custom_dce(const esp_modem_dce_config_t *dce_config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
|
||||
{
|
||||
return dce_factory::Factory::create_unique_dce_from<SIM7600_WITH_TIME, DCE *>(dce_config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This API is only needed for extending standard C-API, since we added get_time() method to our CustomModule
|
||||
*
|
||||
* @note This header is included from esp_modem_c_api.cpp, so it could use ESP_MODEM_C_API_STR_MAX macro
|
||||
* indicating maximum C-API string size
|
||||
*
|
||||
* @note In order to access the newly added API get_time(), we have to static_cast<> the GenericModule from DCE
|
||||
* to our CustomModule.
|
||||
* Alternatively we could use the modem Factory to build our specific DCE_T<CustomModule>, but in that case
|
||||
* we couldn't use our C-API wrappers which expect DCE type, DCE_T<GenericModule> with lib commands (this alternative
|
||||
* is cleaner, but more suitable for C++ users)
|
||||
*/
|
||||
extern "C" esp_err_t esp_modem_get_time(esp_modem_dce_t *dce_wrap, char *p_time)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
std::string time{ESP_MODEM_C_API_STR_MAX};
|
||||
auto ret = command_response_to_esp_err(static_cast<SIM7600_WITH_TIME *>(dce_wrap->dce->get_module())->get_time(time));
|
||||
if (ret == ESP_OK && !time.empty()) {
|
||||
strlcpy(p_time, time.c_str(), ESP_MODEM_C_API_STR_MAX);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -37,6 +37,10 @@ static const int CONNECT_BIT = BIT0;
|
||||
static const int GOT_DATA_BIT = BIT2;
|
||||
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
|
||||
esp_err_t esp_modem_get_time(esp_modem_dce_t *dce_wrap, char *p_time);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
|
||||
#include "esp_modem_usb_c_api.h"
|
||||
#include "esp_modem_usb_config.h"
|
||||
@ -192,6 +196,9 @@ void app_main(void)
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
|
||||
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM7600, &dte_config, &dce_config, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem with custom module...");
|
||||
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_CUSTOM, &dte_config, &dce_config, esp_netif);
|
||||
#else
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for a generic module...");
|
||||
esp_modem_dce_t *dce = esp_modem_new(&dte_config, &dce_config, esp_netif);
|
||||
@ -258,6 +265,18 @@ void app_main(void)
|
||||
}
|
||||
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
|
||||
{
|
||||
char time[64];
|
||||
err = esp_modem_get_time(dce, time);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_modem_get_time failed with %d %s", err, esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_modem_get_time: %s", time);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_SEND_MSG
|
||||
if (esp_modem_sms_txt_mode(dce, true) != ESP_OK || esp_modem_sms_character_set(dce) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Setting text mode or GSM character set failed");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -12,6 +12,7 @@
|
||||
* @brief DCE modem factory
|
||||
*/
|
||||
|
||||
#define esp_modem_create_dce_from dce_factory::Factory::create_unique_dce_from
|
||||
|
||||
namespace esp_modem::dce_factory {
|
||||
|
||||
@ -236,12 +237,12 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T_Module>
|
||||
static std::unique_ptr<DCE> create_unique_dce_from(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
template <typename T_Module, typename Ptr = std::unique_ptr<DCE>>
|
||||
static Ptr create_unique_dce_from(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
return build_generic_DCE<T_Module, DCE, Ptr>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -53,6 +53,7 @@ typedef enum esp_modem_dce_device {
|
||||
ESP_MODEM_DCE_SIM7000,
|
||||
ESP_MODEM_DCE_BG96,
|
||||
ESP_MODEM_DCE_SIM800,
|
||||
ESP_MODEM_DCE_CUSTOM
|
||||
} esp_modem_dce_device_t;
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "uart_terminal.hpp"
|
||||
@ -14,7 +15,6 @@
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
#include "esp_private/c_api_wrapper.hpp"
|
||||
#include "cstring"
|
||||
|
||||
#ifndef ESP_MODEM_C_API_STR_MAX
|
||||
#define ESP_MODEM_C_API_STR_MAX 64
|
||||
@ -24,6 +24,10 @@
|
||||
size_t strlcpy(char *dest, const char *src, size_t len);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE
|
||||
#include CONFIG_ESP_MODEM_CUSTOM_MODULE_HEADER
|
||||
#endif
|
||||
|
||||
//
|
||||
// C API definitions
|
||||
using namespace esp_modem;
|
||||
@ -40,8 +44,15 @@ extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, con
|
||||
return nullptr;
|
||||
}
|
||||
dce_wrap->dte = dte;
|
||||
dce_factory::Factory f(convert_modem_enum(module));
|
||||
dce_wrap->dce = f.build(dce_config, std::move(dte), netif);
|
||||
#ifdef CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE
|
||||
if (module == ESP_MODEM_DCE_CUSTOM) {
|
||||
dce_wrap->dce = esp_modem_create_custom_dce(dce_config, dte, netif);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
|
@ -110,6 +110,10 @@ commands might have a different implementation. Adding a new device
|
||||
means to provide a new implementation as a class derived from
|
||||
``GenericModule``, where we could add new commands or modify the
|
||||
existing ones.
|
||||
If you have to support a custom device with C-API, please refer to
|
||||
the example ``examples/pppos_client`` and enable ``ESP_MODEM_ADD_CUSTOM_MODULE``.
|
||||
For advanced use-case, mainly with C++ API and/or usage of esp_modem's
|
||||
Factory class, please read <advanced_api>.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
Reference in New Issue
Block a user