esp-modem(Docs): Update documentation and minor fixes

This commit is contained in:
David Čermák
2021-05-26 16:41:19 +08:00
committed by David Cermak
parent 28433de4ad
commit e0e65856f0
24 changed files with 139 additions and 350 deletions

View File

@ -1,6 +1,6 @@
# ESP MODEM # ESP MODEM
The `esp-modem` component is a managed component for `esp-idf` that could be used for communication with GSM/LTE modems The `esp-modem` component is a managed component for `esp-idf` that is used for communication with GSM/LTE modems
that support AT commands and PPP protocol as a network interface. that support AT commands and PPP protocol as a network interface.
## Examples ## Examples

View File

@ -36,11 +36,11 @@ After the object is created, the application interaction with the DCE is in
* switching between data and command mode * switching between data and command mode
### DTE ### DTE
Is an abstraction of the connected interface. Current implementation supports only UART Is an abstraction of the physical interface connected to the modem. Current implementation supports only UART
### PPP ### PPP netif
Is used to connect the specific network interface to the modem data mode. Currently implementation supports only PPPoS protocol. Is used to attach the specific network interface to a network communication protocol used by the modem. Currently implementation supports only PPPoS protocol.
### Module ### Module
@ -48,7 +48,7 @@ Abstraction of the specific modem device. Currently the component supports SIM80
## Use cases ## Use cases
Users could interact with the esp-modem using the DCE's interface, to basically Users interact with the esp-modem using the DCE's interface, to basically
* Switch between command and data mode to connect to the internet via cellular network. * Switch between command and data mode to connect to the internet via cellular network.
* Send various commands to the device (e.g. send SMS) * Send various commands to the device (e.g. send SMS)
@ -57,7 +57,7 @@ IP address changes.
Common use cases of the esp-modem are also listed as the examples: Common use cases of the esp-modem are also listed as the examples:
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker. * `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
* `examples/modem_console` -- is an example to exercise all possible modules commands in a console application. * `examples/modem_console` -- is an example to exercise all possible module commands in a console application.
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP * `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP
that forwards packets (and uses NAT) to and from the PPPoS connection. that forwards packets (and uses NAT) to and from the PPPoS connection.
@ -66,7 +66,7 @@ Common use cases of the esp-modem are also listed as the examples:
### CMUX ### CMUX
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode, Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
after creating multiple virtual terminals, designating some of them solely to the data mode, while other to command mode. after creating multiple virtual terminals, designating some of them solely to data mode, others solely to command mode.
### DTE's ### DTE's

View File

@ -26,7 +26,7 @@ Create custom module
Creating a custom module is necessary if the application needs to use a specific device that is not supported Creating a custom module is necessary if the application needs to use a specific device that is not supported
and their commands differ from any of the supported devices. In this case it is recommended to define a new class and their commands differ from any of the supported devices. In this case it is recommended to define a new class
representing this specific device and derive from the :cpp:class:`GenericModule`. In order to instantiate representing this specific device and derive from the :cpp:class:`GenericModule`. In order to instantiate
the appropriate DCE of this module, application could use :ref:`the DCE factory<dce_factory>`, but build the DCE with the appropriate DCE of this module, application could use :ref:`the DCE factory<dce_factory>`, and build the DCE with
the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`. the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`.
Please refer to the implementation of the existing modules. Please refer to the implementation of the existing modules.
@ -41,7 +41,7 @@ Create new communication interface
In order to connect to a device using an unsupported interface (e.g. SPI or I2C), it is necessary to implement In order to connect to a device using an unsupported interface (e.g. SPI or I2C), it is necessary to implement
a custom DTE object and supply it into :ref:`the DCE factory<dce_factory>`. The DCE is typically created in two steps: a custom DTE object and supply it into :ref:`the DCE factory<dce_factory>`. The DCE is typically created in two steps:
- Define and create the corresponding terminal, which can communicate on the custom interface. This terminal should support basic IO methods defined in :cpp:class:`esp_modem::Terminal` and derive from it. - Define and create the corresponding terminal, which communicates on the custom interface. This terminal should support basic IO methods defined in :cpp:class:`esp_modem::Terminal` and derive from it.
- Create the DTE which uses the custom Terminal - Create the DTE which uses the custom Terminal
Please refer to the implementation of the existing UART DTE. Please refer to the implementation of the existing UART DTE.

View File

@ -1,15 +1,15 @@
.. _c_api: .. _c_api:
API Guide for C interface C API Documentation
========================= ===================
C API is very simple and consist of these two basic parts: The C API is very simple and consist of these two basic parts:
- :ref:`lifecycle_api` - :ref:`lifecycle_api`
- :ref:`modem_commands` - :ref:`modem_commands`
Typical application workflow is to: The Typical application workflow is to:
- Create a DCE instance (using :cpp:func:`esp_modem_new`) - Create a DCE instance (using :cpp:func:`esp_modem_new`)
- Call specific functions to issue AT commands (:ref:`modem_commands`) - Call specific functions to issue AT commands (:ref:`modem_commands`)

View File

@ -1,25 +0,0 @@
- :cpp:func:`esp_modem::DCE::sync`
- :cpp:func:`esp_modem::DCE::get_operator_name`
- :cpp:func:`esp_modem::DCE::store_profile`
- :cpp:func:`esp_modem::DCE::set_pin`
- :cpp:func:`esp_modem::DCE::read_pin`
- :cpp:func:`esp_modem::DCE::set_echo`
- :cpp:func:`esp_modem::DCE::sms_txt_mode`
- :cpp:func:`esp_modem::DCE::sms_character_set`
- :cpp:func:`esp_modem::DCE::send_sms`
- :cpp:func:`esp_modem::DCE::resume_data_mode`
- :cpp:func:`esp_modem::DCE::set_pdp_context`
- :cpp:func:`esp_modem::DCE::set_command_mode`
- :cpp:func:`esp_modem::DCE::set_cmux`
- :cpp:func:`esp_modem::DCE::get_imsi`
- :cpp:func:`esp_modem::DCE::get_imei`
- :cpp:func:`esp_modem::DCE::get_module_name`
- :cpp:func:`esp_modem::DCE::set_data_mode`
- :cpp:func:`esp_modem::DCE::get_signal_quality`
- :cpp:func:`esp_modem::DCE::set_flow_control`
- :cpp:func:`esp_modem::DCE::hang_up`
- :cpp:func:`esp_modem::DCE::get_battery_status`
- :cpp:func:`esp_modem::DCE::power_down`
- :cpp:func:`esp_modem::DCE::reset`
- :cpp:func:`esp_modem::DCE::set_baud`

View File

@ -1,99 +0,0 @@
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > c_api.h
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
// call parametrs by names for documentation
// --- DCE command documentation starts here ---
/**
* @brief Sends the initial AT sequence to sync up with the device
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sync (); /**
* @brief Reads the operator name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_operator_name (char* name); /**
* @brief Stores current user profile
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_store_profile (); /**
* @brief Sets the supplied PIN code
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/command_result esp_modem_set_pin (const char* pin); /**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_read_pin (bool* pin_ok); /**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_echo (const bool echo_on); /**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sms_txt_mode (const bool txt); /**
* @brief Sets the default (GSM) charater set
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sms_character_set (); /**
* @brief Sends SMS message in txt mode
* @param[in] number Phone number to send the message to
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_send_sms (const char* number, const char* message); /**
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_resume_data_mode (); /**
* @brief Sets php context
* @param[in] x PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_pdp_context (struct PdpContext* x); /**
* @brief Switches to the command mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_command_mode (); /**
* @brief Switches to the CMUX mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_cmux (); /**
* @brief Reads the IMSI number
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_imsi (char* imsi); /**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_imei (char* imei); /**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_module_name (char* name); /**
* @brief Sets the modem to data mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_data_mode (); /**
* @brief Get Signal quality
* @param[out] rssi signal strength indication
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_signal_quality (int* rssi, int* ber); /**
* @brief Sets HW control flow
* @param[in] dce_flow 0=none, 2=RTS hw flow control of DCE
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_flow_control (int dce_flow, int dte_flow); /**
* @brief Hangs up current data call
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_hang_up (); /**
* @brief Get voltage levels of modem power up circuitry
* @param[out] voltage Current status in mV
* @param[out] bcs charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_battery_status (int* voltage, int* bcs, int* bcl); /**
* @brief Power down the module
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_power_down (); /**
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_reset (); /**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_baud (int baud);

View File

@ -1,111 +0,0 @@
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > c_api.h
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
// call parametrs by names for documentation
// --- DCE command documentation starts here ---
class esp_modem::DCE: public DCE_T<GenericModule> {
public:
using DCE_T<GenericModule>::DCE_T;
/**
* @brief Sends the initial AT sequence to sync up with the device
* @return OK, FAIL or TIMEOUT
*/ command_result sync (); /**
* @brief Reads the operator name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result get_operator_name (std::string& name); /**
* @brief Stores current user profile
* @return OK, FAIL or TIMEOUT
*/ command_result store_profile (); /**
* @brief Sets the supplied PIN code
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/command_result set_pin (const std::string& pin); /**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/ command_result read_pin (bool& pin_ok); /**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/ command_result set_echo (const bool echo_on); /**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/ command_result sms_txt_mode (const bool txt); /**
* @brief Sets the default (GSM) charater set
* @return OK, FAIL or TIMEOUT
*/ command_result sms_character_set (); /**
* @brief Sends SMS message in txt mode
* @param[in] number Phone number to send the message to
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/ command_result send_sms (const std::string& number, const std::string& message); /**
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
* @return OK, FAIL or TIMEOUT
*/ command_result resume_data_mode (); /**
* @brief Sets php context
* @param[in] x PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/ command_result set_pdp_context (PdpContext& x); /**
* @brief Switches to the command mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_command_mode (); /**
* @brief Switches to the CMUX mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_cmux (); /**
* @brief Reads the IMSI number
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/ command_result get_imsi (std::string& imsi); /**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/ command_result get_imei (std::string& imei); /**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result get_module_name (std::string& name); /**
* @brief Sets the modem to data mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_data_mode (); /**
* @brief Get Signal quality
* @param[out] rssi signal strength indication
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/ command_result get_signal_quality (int& rssi, int& ber); /**
* @brief Sets HW control flow
* @param[in] dce_flow 0=none, 2=RTS hw flow control of DCE
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/ command_result set_flow_control (int dce_flow, int dte_flow); /**
* @brief Hangs up current data call
* @return OK, FAIL or TIMEOUT
*/ command_result hang_up (); /**
* @brief Get voltage levels of modem power up circuitry
* @param[out] voltage Current status in mV
* @param[out] bcs charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/ command_result get_battery_status (int& voltage, int& bcs, int& bcl); /**
* @brief Power down the module
* @return OK, FAIL or TIMEOUT
*/ command_result power_down (); /**
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/ command_result reset (); /**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/ command_result set_baud (int baud);
};

View File

@ -19,5 +19,5 @@ doxygen
# Generate the docs # Generate the docs
python -u -m sphinx.cmd.build -b html . html python -u -m sphinx.cmd.build -b html . html
# Cleanup the doxygen xml's # Cleanup the doxygen xml's and temporary headers
rm -rf xml rm -rf xml esp_modem_api_commands.h esp_modem_dce.hpp cxx_api_links.rst

View File

@ -31,9 +31,16 @@ menu "Example Configuration"
help help
Set APN (Access Point Name), a logical name to choose data network Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_NEED_SIM_PIN
bool "SIM PIN needed"
default n
help
Enable to set SIM PIN before starting the example
config EXAMPLE_SIM_PIN config EXAMPLE_SIM_PIN
string "Set SIM PIN" string "Set SIM PIN"
default "1234" default "1234"
depends on EXAMPLE_NEED_SIM_PIN
help help
Pin to unlock the SIM Pin to unlock the SIM

View File

@ -93,7 +93,6 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
void wifi_init_softap(void) void wifi_init_softap(void)
{ {
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_init(&cfg));

View File

@ -19,40 +19,28 @@ using namespace esp_modem;
using namespace esp_modem::dce_factory; using namespace esp_modem::dce_factory;
class NetModule; class NetModule;
typedef DCE_T<NetModule> NetDCE; using NetDCE = DCE_T<NetModule>;
/**
* @brief Local network object used to setup PPP network
*/
class PPPNetwork {
public:
esp_err_t init(esp_netif_t *netif, const std::string& apn, const std::string &pin_number);
void deinit();
NetDCE * get_dce();
private:
NetDCE *dce;
};
/** /**
* @brief The PPP network is a singleton, allocate statically here * @brief The PPP network is a singleton, allocate statically here
*/ */
static PPPNetwork ppp_network;
/** /**
* @brief Custom factory for creating NetDCE and NetModule * @brief Custom factory for creating NetDCE and NetModule
*/ */
class NetDCE_Factory: public Factory { class NetDCE_Factory: public Factory {
public: public:
template <typename T, typename ...Args> template <typename Module, typename ...Args>
static DCE_T<T>* create(const config *cfg, Args&&... args) static DCE_T<Module>* create(const config *cfg, Args&&... args)
{ {
return build_generic_DCE<T>(cfg, std::forward<Args>(args)...); return build_generic_DCE<Module>(cfg, std::forward<Args>(args)...);
} }
template <typename T, typename ...Args> template <typename Module, typename ...Args>
static std::shared_ptr<T> create_module(const config *cfg, Args&&... args) static std::shared_ptr<Module> create_module(const config *cfg, Args&&... args)
{ {
return build_shared_module<T>(cfg, std::forward<Args>(args)...); return build_shared_module<Module>(cfg, std::forward<Args>(args)...);
} }
}; };
@ -67,7 +55,7 @@ public:
explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg): explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg):
dte(std::move(dte)), apn(std::string(cfg->apn)) {} dte(std::move(dte)), apn(std::string(cfg->apn)) {}
bool setup_data_mode() override [[nodiscard]] bool setup_data_mode() override
{ {
PdpContext pdp(apn); PdpContext pdp(apn);
if (set_pdp_context(pdp) != command_result::OK) if (set_pdp_context(pdp) != command_result::OK)
@ -77,18 +65,20 @@ public:
bool set_mode(modem_mode mode) override bool set_mode(modem_mode mode) override
{ {
if (mode == modem_mode::DATA_MODE) { switch (mode) {
if (set_data_mode() != command_result::OK) case esp_modem::modem_mode::DATA_MODE:
if (set_data_mode() != command_result::OK) {
return resume_data_mode() == command_result::OK; return resume_data_mode() == command_result::OK;
}
return true; return true;
} case esp_modem::modem_mode::COMMAND_MODE:
if (mode == modem_mode::COMMAND_MODE) {
return set_command_mode() == command_result::OK; return set_command_mode() == command_result::OK;
} default:
return false; return false;
} }
}
bool init(const std::string& pin) [[maybe_unused]] bool init_sim(const std::string& pin)
{ {
// switch to command mode (in case we were in PPP mode) // switch to command mode (in case we were in PPP mode)
static_cast<void>(set_command_mode()); // ignore the potential failure, as we might be in command mode after startup static_cast<void>(set_command_mode()); // ignore the potential failure, as we might be in command mode after startup
@ -118,22 +108,33 @@ private:
}; };
esp_err_t PPPNetwork::init(esp_netif_t *netif, const std::string& apn, const std::string &pin_number) /**
* @brief Implement the C-API for the AP-2-PPP functionality
*/
namespace {
/**
* @brief Local network object used to setup PPP network
*/
NetDCE *dce = nullptr;
extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
{ {
// configure // configure
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.uart_config.rx_buffer_size = 16384; dte_config.uart_config.rx_buffer_size = 16384;
dte_config.uart_config.tx_buffer_size = 2048; dte_config.uart_config.tx_buffer_size = 2048;
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(apn.c_str()); esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
// create DTE and minimal network DCE // create DTE and minimal network DCE
auto uart_dte = create_uart_dte(&dte_config); auto uart_dte = create_uart_dte(&dte_config);
// create the specific device (and initialize it) // create the specific device (and initialize it)
auto dev = NetDCE_Factory::create_module<NetModule>(&dce_config, uart_dte, netif); auto dev = NetDCE_Factory::create_module<NetModule>(&dce_config, uart_dte, netif);
if (!dev->init(pin_number)) #if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
if (!dev->init_sim(CONFIG_EXAMPLE_SIM_PIN))
return ESP_FAIL; return ESP_FAIL;
#endif
// now create the DCE from our already existent device // now create the DCE from our already existent device
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif, dev); dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif, dev);
if (dce == nullptr) if (dce == nullptr)
@ -141,36 +142,20 @@ esp_err_t PPPNetwork::init(esp_netif_t *netif, const std::string& apn, const std
return ESP_OK; return ESP_OK;
} }
void PPPNetwork::deinit() extern "C" void modem_start_network()
{
dce->set_mode(esp_modem::modem_mode::DATA_MODE);
}
extern "C" void modem_stop_network()
{
dce->set_mode(esp_modem::modem_mode::COMMAND_MODE);
}
extern "C" void modem_deinit_network()
{ {
free(dce); free(dce);
dce = nullptr; dce = nullptr;
} }
NetDCE *PPPNetwork::get_dce()
{
return dce;
}
/**
* @brief Implement the C-API for the AP-2-PPP functionality
*/
extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
{
return ppp_network.init(netif, CONFIG_EXAMPLE_MODEM_PPP_APN, CONFIG_EXAMPLE_SIM_PIN);
}
extern "C" void modem_start_network()
{
ppp_network.get_dce()->set_mode(esp_modem::modem_mode::DATA_MODE);
}
extern "C" void modem_stop_network()
{
ppp_network.get_dce()->set_mode(esp_modem::modem_mode::COMMAND_MODE);
}
extern "C" void modem_deinit_network()
{
ppp_network.deinit();
} }

View File

@ -15,7 +15,7 @@
using namespace esp_modem; using namespace esp_modem;
[[maybe_unused]] static const char *TAG = "linux_modem_main"; [[maybe_unused]] constexpr auto TAG = "linux_modem_main";
int main() int main()

View File

@ -1,4 +1,4 @@
# PPPoS simple client example # Modem console example
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)

View File

@ -21,35 +21,37 @@ ConsoleCommand::ConsoleCommand(const char* command, const char* help, const std:
void ConsoleCommand::RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args) void ConsoleCommand::RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args)
{ {
assert(last_command <= MAX_REPEAT_NR); assert(last_command <= MAX_REPEAT_NR);
void * common_arg = nullptr; arg_type common_arg = { };
for (auto it: args) { for (auto& it: args) {
switch(it.type) { switch(it.type) {
case ARG_END: case ARG_END:
break; break;
case STR0: case STR0:
common_arg = arg_str0(it.shortopts, it.longopts, it.datatype, it.glossary); common_arg.str = arg_str0(it.shortopts, it.longopts, it.datatype, it.glossary);
break; break;
case STR1: case STR1:
common_arg = arg_str1(it.shortopts, it.longopts, it.datatype, it.glossary); common_arg.str = arg_str1(it.shortopts, it.longopts, it.datatype, it.glossary);
break; break;
case INT0: case INT0:
common_arg = arg_int0(it.shortopts, it.longopts, it.datatype, it.glossary); common_arg.intx = arg_int0(it.shortopts, it.longopts, it.datatype, it.glossary);
break; break;
case INT1: case INT1:
common_arg = arg_int1(it.shortopts, it.longopts, it.datatype, it.glossary); common_arg.intx = arg_int1(it.shortopts, it.longopts, it.datatype, it.glossary);
break; break;
case LIT0: case LIT0:
common_arg = arg_lit0(it.shortopts, it.longopts, it.glossary); common_arg.lit = arg_lit0(it.shortopts, it.longopts, it.glossary);
break; break;
} }
if (common_arg) { if (common_arg.is_null()) {
arg_table.emplace_back(common_arg); arg_table.emplace_back(common_arg);
} else { } else {
ESP_LOGE(TAG, "Creating argument parser failed for %s", it.glossary); ESP_LOGE(TAG, "Creating argument parser failed for %s", it.glossary);
abort(); abort();
} }
} }
arg_table.emplace_back( arg_end(1));
arg_type end = { .end = arg_end(1) };
arg_table.emplace_back(end);
const esp_console_cmd_t command_def = { const esp_console_cmd_t command_def = {
.command = command, .command = command,
.help = help, .help = help,
@ -64,13 +66,13 @@ void ConsoleCommand::RegisterCommand(const char* command, const char* help, cons
int ConsoleCommand::get_count(int index) int ConsoleCommand::get_count(int index)
{ {
return ((struct arg_str *)arg_table[index])->count; return (arg_table[index].str)->count;
} }
std::string ConsoleCommand::get_string(int index) std::string ConsoleCommand::get_string(int index)
{ {
if (get_count(index) > 0) { if (get_count(index) > 0) {
return std::string(((struct arg_str *)arg_table[index])->sval[0]); return std::string(arg_table[index].str->sval[0]);
} }
return std::string(); return std::string();
} }
@ -78,17 +80,17 @@ std::string ConsoleCommand::get_string(int index)
int ConsoleCommand::get_int(int index) int ConsoleCommand::get_int(int index)
{ {
if (get_count(index) > 0) { if (get_count(index) > 0) {
return *((struct arg_int *)arg_table[index])->ival; return *(arg_table[index].intx)->ival;
} }
return -1; return -1;
} }
int ConsoleCommand::command_func(int argc, char **argv) { int ConsoleCommand::command_func(int argc, char **argv) {
void * plain_arg_array = &arg_table[0]; arg_type* plain_arg_array = &arg_table[0];
int nerrors = arg_parse(argc, argv, (void **)plain_arg_array); int nerrors = arg_parse(argc, argv, (void **)plain_arg_array);
if (nerrors != 0) { if (nerrors != 0) {
arg_print_errors(stderr, (struct arg_end *) arg_table.back(), argv[0]); arg_print_errors(stderr, arg_table.back().end, argv[0]);
return 1; return 1;
} }
if (func) { if (func) {

View File

@ -53,6 +53,19 @@ class StaticCommands;
* @brief This class simplifies console command definition in more object wise fashion * @brief This class simplifies console command definition in more object wise fashion
*/ */
class ConsoleCommand { class ConsoleCommand {
/**
* @brief Common argument types to be stored internally for parsing later
*/
using arg_type =
union {
struct arg_int *intx;
struct arg_str *str;
struct arg_lit *lit;
struct arg_end *end;
void *__raw_ptr;
bool is_null() const { return __raw_ptr; }
};
friend class StaticCommands; friend class StaticCommands;
public: public:
/** /**
@ -91,7 +104,7 @@ private:
void RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args); void RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args);
template<typename T> static constexpr size_t index_arg(CommandArgs T::*member) template<typename T> static constexpr size_t index_arg(CommandArgs T::*member)
{ return ((uint8_t *)&((T*)nullptr->*member) - (uint8_t *)nullptr)/sizeof(CommandArgs); } { return ((uint8_t *)&((T*)nullptr->*member) - (uint8_t *)nullptr)/sizeof(CommandArgs); }
std::vector<void*> arg_table; std::vector<arg_type> arg_table;
int command_func(int argc, char **argv); int command_func(int argc, char **argv);
static int last_command; static int last_command;

View File

@ -157,11 +157,11 @@ extern "C" void app_main(void)
auto cmd = c->get_string_of(&GenericCommandArgs::cmd); auto cmd = c->get_string_of(&GenericCommandArgs::cmd);
auto timeout = c->get_count_of(&GenericCommandArgs::timeout) ? c->get_int_of(&GenericCommandArgs::timeout) auto timeout = c->get_count_of(&GenericCommandArgs::timeout) ? c->get_int_of(&GenericCommandArgs::timeout)
: 1000; : 1000;
ESP_LOGI(TAG, "Sending command %s with timeout %d", cmd.c_str(), timeout);
auto pattern = c->get_string_of(&GenericCommandArgs::pattern); auto pattern = c->get_string_of(&GenericCommandArgs::pattern);
if (c->get_count_of(&GenericCommandArgs::no_cr) == 0) { if (c->get_count_of(&GenericCommandArgs::no_cr) == 0) {
cmd += '\r'; cmd += '\r';
} }
ESP_LOGI(TAG, "Sending command %s with timeout %d", cmd.c_str(), timeout);
CHECK_ERR(dce->command(cmd, [&](uint8_t *data, size_t len) { CHECK_ERR(dce->command(cmd, [&](uint8_t *data, size_t len) {
std::string response((char *) data, len); std::string response((char *) data, len);
ESP_LOGI(TAG, "%s", response.c_str()); ESP_LOGI(TAG, "%s", response.c_str());

View File

@ -51,7 +51,7 @@ class CMuxInstance;
/** /**
* @brief CMux class which consumes the original terminal and creates multiple virtual terminals from it. * @brief CMux class which consumes the original terminal and creates multiple virtual terminals from it.
* This class is not usable applicable as a DTE terminal * This class itself is not usable as a DTE terminal, only via its instances defined in `CMuxInstance`
*/ */
class CMux { class CMux {
public: public:
@ -100,7 +100,7 @@ public:
void stop() override { } void stop() override { }
private: private:
std::shared_ptr<CMux> cmux; std::shared_ptr<CMux> cmux;
int instance; size_t instance;
}; };
/** /**

View File

@ -88,7 +88,7 @@ protected:
* @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported * @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported
* defices and most common modems, as well. * defices and most common modems, as well.
*/ */
class DCE: public DCE_T<GenericModule> { class DCE : public DCE_T<GenericModule> {
public: public:
using DCE_T<GenericModule>::DCE_T; using DCE_T<GenericModule>::DCE_T;

View File

@ -45,7 +45,7 @@ enum class terminal_error {
}; };
/** /**
* @brief Terminal interface. All communication interfaces must comply this interface in order to be used as a DTE * @brief Terminal interface. All communication interfaces must comply to this interface in order to be used as a DTE
*/ */
class Terminal { class Terminal {
public: public:
@ -53,7 +53,7 @@ public:
void set_error_cb(std::function<void(terminal_error)> f) { on_error = std::move(f); } void set_error_cb(std::function<void(terminal_error)> f) { on_error = std::move(f); }
virtual void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) { on_data = std::move(f); } virtual void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) { on_read = std::move(f); }
/** /**
* @brief Writes data to the terminal * @brief Writes data to the terminal
@ -76,7 +76,7 @@ public:
virtual void stop() = 0; virtual void stop() = 0;
protected: protected:
std::function<bool(uint8_t *data, size_t len)> on_data; std::function<bool(uint8_t *data, size_t len)> on_read;
std::function<void(terminal_error)> on_error; std::function<void(terminal_error)> on_error;
}; };

View File

@ -17,6 +17,10 @@
#include "driver/uart.h" #include "driver/uart.h"
#include "esp_modem_dce_config.h" #include "esp_modem_dce_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/** /**
* @defgroup ESP_MODEM_CONFIG * @defgroup ESP_MODEM_CONFIG
* @brief Configuration structures for DTE and DCE * @brief Configuration structures for DTE and DCE
@ -56,6 +60,9 @@ struct esp_modem_uart_term_config {
int event_queue_size; /*!< UART Event Queue Size, set to 0 if no event queue needed */ int event_queue_size; /*!< UART Event Queue Size, set to 0 if no event queue needed */
}; };
// Forward declare the resource struct
struct esp_modem_vfs_resource;
/** /**
* @brief VFS configuration structure * @brief VFS configuration structure
* *
@ -116,4 +123,8 @@ typedef struct esp_modem_dte_config esp_modem_dte_config_t;
* @} * @}
*/ */
#ifdef __cplusplus
}
#endif
#endif // _ESP_MODEM_CONFIG_H_ #endif // _ESP_MODEM_CONFIG_H_

View File

@ -15,6 +15,10 @@
#ifndef _ESP_MODEM_DCE_CONFIG_H_ #ifndef _ESP_MODEM_DCE_CONFIG_H_
#define _ESP_MODEM_DCE_CONFIG_H_ #define _ESP_MODEM_DCE_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup ESP_MODEM_CONFIG /** @addtogroup ESP_MODEM_CONFIG
* @{ * @{
*/ */
@ -41,5 +45,8 @@ struct esp_modem_dce_config {
* @} * @}
*/ */
#ifdef __cplusplus
}
#endif
#endif // _ESP_MODEM_DCE_CONFIG_H_ #endif // _ESP_MODEM_DCE_CONFIG_H_

View File

@ -213,7 +213,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(x, baud))
#define _ARG(param, name) name #define _ARG(param, name) name
// --- DCE command documentation starts here --- // --- DCE command documentation starts here ---
#ifdef __cplusplus #ifdef __cplusplus
class esp_modem::DCE: public DCE_T<GenericModule> { class esp_modem::DCE : public DCE_T<GenericModule> {
public: public:
using DCE_T<GenericModule>::DCE_T; using DCE_T<GenericModule>::DCE_T;
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, ...) return_type name (__VA_ARGS__); #define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, ...) return_type name (__VA_ARGS__);

View File

@ -58,7 +58,7 @@ public:
int read(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 { void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
on_data = std::move(f); on_read = std::move(f);
signal.set(TASK_PARAMS); signal.set(TASK_PARAMS);
} }
@ -94,7 +94,7 @@ FdTerminal::FdTerminal(const esp_modem_dte_config *config) :
void FdTerminal::task() void FdTerminal::task()
{ {
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr; std::function<bool(uint8_t *data, size_t len)> on_read_priv = nullptr;
signal.set(TASK_INIT); signal.set(TASK_INIT);
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY); signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
if (signal.is_any(TASK_STOP)) { if (signal.is_any(TASK_STOP)) {
@ -113,7 +113,7 @@ void FdTerminal::task()
s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv); s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv);
if (signal.is_any(TASK_PARAMS)) { if (signal.is_any(TASK_PARAMS)) {
on_data_priv = on_data; on_read_priv = on_read;
signal.clear(TASK_PARAMS); signal.clear(TASK_PARAMS);
} }
@ -123,9 +123,9 @@ void FdTerminal::task()
// ESP_LOGV(TAG, "Select exited with timeout"); // ESP_LOGV(TAG, "Select exited with timeout");
} else { } else {
if (FD_ISSET(f.fd, &rfds)) { if (FD_ISSET(f.fd, &rfds)) {
if (on_data_priv) { if (on_read_priv) {
if (on_data_priv(nullptr, 0)) { if (on_read_priv(nullptr, 0)) {
on_data_priv = nullptr; on_read_priv = nullptr;
} }
} }
} }

View File

@ -65,7 +65,7 @@ public:
int read(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 { void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
on_data = std::move(f); on_read = std::move(f);
signal.set(TASK_PARAMS); signal.set(TASK_PARAMS);
} }
@ -106,7 +106,7 @@ std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *confi
} }
void UartTerminal::task() { void UartTerminal::task() {
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr; std::function<bool(uint8_t *data, size_t len)> on_read_priv = nullptr;
uart_event_t event; uart_event_t event;
size_t len; size_t len;
signal.set(TASK_INIT); signal.set(TASK_INIT);
@ -117,15 +117,15 @@ void UartTerminal::task() {
while (signal.is_any(TASK_START)) { while (signal.is_any(TASK_START)) {
if (get_event(event, 100)) { if (get_event(event, 100)) {
if (signal.is_any(TASK_PARAMS)) { if (signal.is_any(TASK_PARAMS)) {
on_data_priv = on_data; on_read_priv = on_read;
signal.clear(TASK_PARAMS); signal.clear(TASK_PARAMS);
} }
switch (event.type) { switch (event.type) {
case UART_DATA: case UART_DATA:
uart_get_buffered_data_len(uart.port, &len); uart_get_buffered_data_len(uart.port, &len);
if (len && on_data_priv) { if (len && on_read_priv) {
if (on_data_priv(nullptr, len)) { if (on_read_priv(nullptr, len)) {
on_data_priv = nullptr; on_read_priv = nullptr;
} }
} }
break; break;