Advanced esp-modem use cases

This chapter outlines basic extensibility of the esp-modem component.

Custom instantiation with DCE factory

It is possible to create a modem handle in many different ways:

  • Build a DCE on top a generic module, user defined module or build the module only (in case the application will only use AT command interface)

  • Create the DCE as a shared, unique or a vanilla pointer

  • Create a generic DCE or a templated DCE_T of a specific module (this could be one of the supported modules or a user defined module)

All the functionality is provided by the DCE factory

group ESP_MODEM_DCE_FACTORY

DCE modem factory.

Enums

enum class ModemType

Specific modem choice when creating by the Factory.

Values:

enumerator GenericModule

Default generic module with the most common commands

enumerator SIM7600

Derived from the GenericModule, specifics applied to SIM7600 model

enumerator SIM7070

Derived from the GenericModule, specifics applied to SIM7070 model

enumerator SIM7000

Derived from the GenericModule, specifics applied to SIM7000 model

enumerator BG96

Derived from the GenericModule, specifics applied to BG69 model

enumerator SIM800

Derived from the GenericModule with specifics applied to SIM800 model

enumerator SQNGM02S

Derived from the GenericModule, specifics applied to GM02S model

class FactoryHelper
#include <esp_modem_dce_factory.hpp>

Helper class for creating a user define pointer in a specific way, either as a plain pointer, shared_ptr or unique_ptr.

template<typename T_Module>
class Creator
#include <esp_modem_dce_factory.hpp>

Creator class for building a DCE_T<Module> in a specific way, either from a Module object or by default from the DTE and netif.

esp_modem::esp_err_exception on invalid arguments

  • std::bad_alloc if failed to allocate

Throws :

class Factory
#include <esp_modem_dce_factory.hpp>

Factory class for creating virtual DCE objects based on the configuration of the supplied module. This could also be used to create a custom module or a DCE_T<module>, provided user app derives from this factory.

Public Functions

template<typename ...Args>
inline std::unique_ptr<DCE> build_unique(const config *cfg, Args&&... args)

Create a default unique_ptr DCE generically, with the chosen module derived from the GenericModule.

Template Parameters:

Args – Arguments to the builder, i.e. constructor of esp_modem::DCE_T class

Parameters:
Returns:

unique_ptr DCE of the created DCE on success

Public Static Functions

template<typename T_Module, typename ...Args>
static inline std::unique_ptr<DCE> build_unique(const config *cfg, Args&&... args)

Create a default unique_ptr DCE in a specific way (from the module)

Template Parameters:
  • Module – Specific Module used in this DCE

  • Args – Arguments to the builder, i.e. constructor of esp_modem::DCE_T class

Parameters:
Returns:

unique_ptr DCE of the created DCE on success

template<typename T_Module, typename ...Args>
static inline DCE *build(const config *cfg, Args&&... args)

Create a DCE.

Template Parameters:
  • Module – Specific Module used in this DCE

  • Args – Arguments to the builder, i.e. constructor of esp_modem::DCE_T class

Parameters:
Returns:

DCE pointer the created DCE on success

Create custom module

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 representing this specific device and derive from the esp_modem::GenericModule (or any other available module, that’s close to your custom device). Then you can create the DCE using the DCE factory public method esp_modem::dce_factory::Factory::create_unique_dce_from().

Please note that the pppos_client example defines a trivial custom DCE which overrides one command, and adds a new command for demonstration purposes only.

It is also possible to create a specific DCE class that would conform to the generic DCE API and use all generic commands, work with commands differently. This might be useful to add some custom preprocessing of commands or replies. Please check the modem_console example with CONFIG_EXAMPLE_MODEM_DEVICE_SHINY=y configuration which demonstrates overriding default command() method to implement URC processing in user space.

Enhanced URC (Unsolicited Result Code) Handling

The ESP modem library provides two interfaces for handling URCs: a legacy callback-based interface and an enhanced interface with granular buffer consumption control. The enhanced interface, available through esp_modem::DCE_T::set_enhanced_urc(), provides complete buffer visibility and allows URC handlers to make precise decisions about buffer consumption. This is particularly useful for processing multi-part responses or when you need to consume only specific portions of the buffer while preserving other data for command processing.

The enhanced URC handler receives a esp_modem::DTE::UrcBufferInfo structure containing the complete buffer context, including what data has been processed, what’s new, and whether a command is currently active. The handler returns a esp_modem::DTE::UrcConsumeInfo structure specifying how much of the buffer to consume: none (wait for more data), partial (consume specific amount), or all (consume entire buffer). This granular control enables sophisticated URC processing scenarios such as line-by-line parsing of chunked HTTP responses or selective processing based on command state.

For applications migrating from the legacy URC interface, the enhanced interface maintains backward compatibility while providing significantly more control over buffer management. The legacy esp_modem::DCE_T::set_urc() method continues to work as before, but new applications should consider using the enhanced interface for better buffer control and processing flexibility.

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 a custom DTE object and supply it into the DCE factory. The DCE is typically created in two steps:

  • Define and create the corresponding terminal, which communicates on the custom interface. This terminal should support basic IO methods defined in esp_modem::Terminal and derive from it.

  • Create the DTE which uses the custom Terminal

Please refer to the implementation of the existing UART DTE.