diff --git a/.github/workflows/modem__build-host-tests.yml b/.github/workflows/modem__build-host-tests.yml
index 675fb9c691..832c744c6f 100644
--- a/.github/workflows/modem__build-host-tests.yml
+++ b/.github/workflows/modem__build-host-tests.yml
@@ -100,3 +100,21 @@ jobs:
         run_executable: false
         run_coverage: false
         pre_run_script: "esp-protocols/components/esp_modem/test/host_test/env.sh"
+
+  esp_modem_generated_commands:
+    if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
+    name: Generated commands compatibility
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout esp-protocols
+        uses: actions/checkout@v4
+      - name: Compat check
+        shell: bash
+        run: |
+          sudo apt-get update -y
+          sudo apt-get install -y astyle
+          cd components/esp_modem
+          find examples/ -type f -regex '.*/generate/.*\.\(hpp\|cpp\)' -exec ./scripts/generate.sh {} \;
+          ./scripts/generate.sh
+          git diff --name-only
+          git diff --quiet
diff --git a/components/esp_modem/CMakeLists.txt b/components/esp_modem/CMakeLists.txt
index fbede40264..c0e37154fa 100644
--- a/components/esp_modem/CMakeLists.txt
+++ b/components/esp_modem/CMakeLists.txt
@@ -15,6 +15,13 @@ else()
 endif()
 
 
+if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
+    set(command_dir "generate") # using in-place macro expansion with AT commands
+else()
+    set(command_dir "command") # using pre-generated AT commands
+endif()
+
+
 set(srcs ${platform_srcs}
         "src/esp_modem_dte.cpp"
         "src/esp_modem_dce.cpp"
@@ -26,12 +33,10 @@ set(srcs ${platform_srcs}
         "src/esp_modem_term_fs.cpp"
         "src/esp_modem_vfs_uart_creator.cpp"
         "src/esp_modem_vfs_socket_creator.cpp"
-        "src/esp_modem_modules.cpp")
-
-set(include_dirs "include")
+        "${command_dir}/src/esp_modem_modules.cpp")
 
 idf_component_register(SRCS "${srcs}"
-                    INCLUDE_DIRS "${include_dirs}"
+                    INCLUDE_DIRS include ${command_dir}/include
                     PRIV_INCLUDE_DIRS private_include
                     REQUIRES ${dependencies})
 
diff --git a/components/esp_modem/Kconfig b/components/esp_modem/Kconfig
index d5b54a8fb8..11bc8e9afb 100644
--- a/components/esp_modem/Kconfig
+++ b/components/esp_modem/Kconfig
@@ -76,4 +76,15 @@ menu "esp-modem"
         help
             If enabled, APIs to add URC handler are available
 
+    config ESP_MODEM_ENABLE_DEVELOPMENT_MODE
+        bool "Use development mode"
+        default n
+        help
+            We use pre-generated headers and sources with common AT commands.
+            This is useful for using ESP-MODEM library with IDEs and code navigation.
+            When developing ESP-MODEM library, it's usually more convenient
+            to work with sources directly, using C-preprocessor's metaprogramming.
+            Set this to 'y' if you're making changes to the actual sources of
+            the AT command definitions (typically in esp_modem_command_declare.inc)
+
 endmenu
diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp
new file mode 100644
index 0000000000..7f076f1948
--- /dev/null
+++ b/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp
@@ -0,0 +1,288 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "cxx_include/esp_modem_dte.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+
+namespace esp_modem {
+namespace dce_commands {
+/**
+ * @defgroup ESP_MODEM_DCE_COMMAND ESP_MODEM DCE command library
+ * @brief Library of the most useful DCE commands
+ */
+/** @addtogroup ESP_MODEM_DCE_COMMAND
+ * @{
+ */
+/**
+ * @brief Generic AT command to be send with pass and fail phrases
+ *
+ * @param t Commandable object (anything that can accept commands)
+ * @param command Command to be sent do the commandable object
+ * @param pass_phrase String to be present in the reply to pass this command
+ * @param fail_phrase String to be present in the reply to fail this command
+ * @param timeout_ms Timeout in ms
+ */
+command_result generic_command(CommandableIf *t, const std::string &command,
+                               const std::string &pass_phrase,
+                               const std::string &fail_phrase, uint32_t timeout_ms);
+/**
+ * @brief Declaration of all commands is generated from esp_modem_command_declare.inc
+ */
+/**
+ * @brief Sends the initial AT sequence to sync up with the device
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result sync(CommandableIf *t );
+/**
+ * @brief Reads the operator name
+ * @param[out] name operator name
+ * @param[out] act access technology
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_operator_name(CommandableIf *t, std::string &name, int &act );
+/**
+ * @brief Stores current user profile
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result store_profile(CommandableIf *t );
+/**
+ * @brief Sets the supplied PIN code
+ * @param[in] pin Pin
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_pin(CommandableIf *t, const std::string &pin );
+/**
+ * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+ * @param[in] cmd String command that's send to DTE
+ * @param[out] out Raw output from DTE
+ * @param[in] pass Pattern in response for the API to return OK
+ * @param[in] fail Pattern in response for the API to return FAIL
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout );
+/**
+ * @brief Execute the supplied AT command
+ * @param[in] cmd AT command
+ * @param[out] out Command output string
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result at(CommandableIf *t, const std::string &cmd, std::string &out, int timeout );
+/**
+ * @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(CommandableIf *t, 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(CommandableIf *t, 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(CommandableIf *t, const bool txt );
+/**
+ * @brief Sets the default (GSM) character set
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result sms_character_set(CommandableIf *t );
+/**
+ * @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(CommandableIf *t, const std::string &number, const std::string &message );
+/**
+ * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result resume_data_mode(CommandableIf *t );
+/**
+ * @brief Sets php context
+ * @param[in] p1 PdP context struct to setup modem cellular connection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_pdp_context(CommandableIf *t, PdpContext &pdp );
+/**
+ * @brief Switches to the command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_command_mode(CommandableIf *t );
+/**
+ * @brief Switches to the CMUX mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_cmux(CommandableIf *t );
+/**
+ * @brief Reads the IMSI number
+ * @param[out] imsi Module's IMSI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_imsi(CommandableIf *t, std::string &imsi );
+/**
+ * @brief Reads the IMEI number
+ * @param[out] imei Module's IMEI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_imei(CommandableIf *t, std::string &imei );
+/**
+ * @brief Reads the module name
+ * @param[out] name module name
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_module_name(CommandableIf *t, std::string &name );
+/**
+ * @brief Sets the modem to data mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_data_mode(CommandableIf *t );
+/**
+ * @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(CommandableIf *t, 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(CommandableIf *t, int dce_flow, int dte_flow );
+/**
+ * @brief Hangs up current data call
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result hang_up(CommandableIf *t );
+/**
+ * @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(CommandableIf *t, int &voltage, int &bcs, int &bcl );
+/**
+ * @brief Power down the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result power_down(CommandableIf *t );
+/**
+ * @brief Reset the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result reset(CommandableIf *t );
+/**
+ * @brief Configures the baudrate
+ * @param[in] baud Desired baud rate of the DTE
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_baud(CommandableIf *t, int baud );
+/**
+ * @brief Force an attempt to connect to a specific operator
+ * @param[in] mode mode of attempt
+ * mode=0 - automatic
+ * mode=1 - manual
+ * mode=2 - deregister
+ * mode=3 - set format for read operation
+ * mode=4 - manual with fallback to automatic
+ * @param[in] format what format the operator is given in
+ * format=0 - long format
+ * format=1 - short format
+ * format=2 - numeric
+ * @param[in] oper the operator to connect to
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_operator(CommandableIf *t, int mode, int format, const std::string &oper );
+/**
+ * @brief Attach or detach from the GPRS service
+ * @param[in] state 1-attach 0-detach
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_network_attachment_state(CommandableIf *t, int state );
+/**
+ * @brief Get network attachment state
+ * @param[out] state 1-attached 0-detached
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_network_attachment_state(CommandableIf *t, int &state );
+/**
+ * @brief What mode the radio should be set to
+ * @param[in] state state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_radio_state(CommandableIf *t, int state );
+/**
+ * @brief Get current radio state
+ * @param[out] state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_radio_state(CommandableIf *t, int &state );
+/**
+ * @brief Set network mode
+ * @param[in] mode preferred mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_network_mode(CommandableIf *t, int mode );
+/**
+ * @brief Preferred network mode (CAT-M and/or NB-IoT)
+ * @param[in] mode preferred selection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_preferred_mode(CommandableIf *t, int mode );
+/**
+ * @brief Set network bands for CAT-M or NB-IoT
+ * @param[in] mode CAT-M or NB-IoT
+ * @param[in] bands bitmap in hex representing bands
+ * @param[in] size size of teh bands bitmap
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_network_bands(CommandableIf *t, const std::string &mode, const int *bands, int size );
+/**
+ * @brief Show network system mode
+ * @param[out] mode current network mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_network_system_mode(CommandableIf *t, int &mode );
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result set_gnss_power_mode(CommandableIf *t, int mode );
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result get_gnss_power_mode(CommandableIf *t, int &mode );
+/**
+ * @brief Following commands that are different for some specific modules
+ */
+command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl);
+command_result set_gnss_power_mode_sim76xx(CommandableIf *t, int mode);
+command_result power_down_sim76xx(CommandableIf *t);
+command_result power_down_sim70xx(CommandableIf *t);
+command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size);
+command_result power_down_sim8xx(CommandableIf *t);
+command_result set_data_mode_alt(CommandableIf *t);
+command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms);
+/**
+ * @}
+ */
+} // dce_commands
+} // esp_modem
diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp
new file mode 100644
index 0000000000..ea18f71a7b
--- /dev/null
+++ b/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp
@@ -0,0 +1,379 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+namespace esp_modem {
+/**
+ * @defgroup ESP_MODEM_DCE
+ * @brief Definition of DCE abstraction
+ */
+/** @addtogroup ESP_MODEM_DCE
+ * @{
+ */
+/**
+ * @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported
+ * devices and most common modems, as well.
+ */
+class DCE : public DCE_T<GenericModule> {
+public:
+    command_result get_operator_name(std::string &name)
+    {
+        return device->get_operator_name(name);
+    }
+    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()
+    {
+        return device->sync();
+    }
+    /**
+     * @brief Reads the operator name
+     * @param[out] name operator name
+     * @param[out] act access technology
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_operator_name(std::string &name, int &act )
+    {
+        return device->get_operator_name(name, act );
+    }
+    /**
+     * @brief Stores current user profile
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result store_profile()
+    {
+        return device->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 )
+    {
+        return device->set_pin(pin );
+    }
+    /**
+     * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+     * @param[in] cmd String command that's send to DTE
+     * @param[out] out Raw output from DTE
+     * @param[in] pass Pattern in response for the API to return OK
+     * @param[in] fail Pattern in response for the API to return FAIL
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout )
+    {
+        return device->at_raw(cmd, out, pass, fail, timeout );
+    }
+    /**
+     * @brief Execute the supplied AT command
+     * @param[in] cmd AT command
+     * @param[out] out Command output string
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result at(const std::string &cmd, std::string &out, int timeout )
+    {
+        return device->at(cmd, out, timeout );
+    }
+    /**
+     * @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 )
+    {
+        return device->read_pin(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 )
+    {
+        return device->set_echo(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 )
+    {
+        return device->sms_txt_mode(txt );
+    }
+    /**
+     * @brief Sets the default (GSM) character set
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result sms_character_set()
+    {
+        return device->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 )
+    {
+        return device->send_sms(number, message );
+    }
+    /**
+     * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result resume_data_mode()
+    {
+        return device->resume_data_mode();
+    }
+    /**
+     * @brief Sets php context
+     * @param[in] p1 PdP context struct to setup modem cellular connection
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_pdp_context(PdpContext &pdp )
+    {
+        return device->set_pdp_context(pdp );
+    }
+    /**
+     * @brief Switches to the command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_command_mode()
+    {
+        return device->set_command_mode();
+    }
+    /**
+     * @brief Switches to the CMUX mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_cmux()
+    {
+        return device->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 )
+    {
+        return device->get_imsi(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 )
+    {
+        return device->get_imei(imei );
+    }
+    /**
+     * @brief Reads the module name
+     * @param[out] name module name
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_module_name(std::string &name )
+    {
+        return device->get_module_name(name );
+    }
+    /**
+     * @brief Sets the modem to data mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_data_mode()
+    {
+        return device->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 )
+    {
+        return device->get_signal_quality(rssi, 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 )
+    {
+        return device->set_flow_control(dce_flow, dte_flow );
+    }
+    /**
+     * @brief Hangs up current data call
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result hang_up()
+    {
+        return device->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 )
+    {
+        return device->get_battery_status(voltage, bcs, bcl );
+    }
+    /**
+     * @brief Power down the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result power_down()
+    {
+        return device->power_down();
+    }
+    /**
+     * @brief Reset the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result reset()
+    {
+        return device->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 )
+    {
+        return device->set_baud(baud );
+    }
+    /**
+     * @brief Force an attempt to connect to a specific operator
+     * @param[in] mode mode of attempt
+     * mode=0 - automatic
+     * mode=1 - manual
+     * mode=2 - deregister
+     * mode=3 - set format for read operation
+     * mode=4 - manual with fallback to automatic
+     * @param[in] format what format the operator is given in
+     * format=0 - long format
+     * format=1 - short format
+     * format=2 - numeric
+     * @param[in] oper the operator to connect to
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_operator(int mode, int format, const std::string &oper )
+    {
+        return device->set_operator(mode, format, oper );
+    }
+    /**
+     * @brief Attach or detach from the GPRS service
+     * @param[in] state 1-attach 0-detach
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_network_attachment_state(int state )
+    {
+        return device->set_network_attachment_state(state );
+    }
+    /**
+     * @brief Get network attachment state
+     * @param[out] state 1-attached 0-detached
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_network_attachment_state(int &state )
+    {
+        return device->get_network_attachment_state(state );
+    }
+    /**
+     * @brief What mode the radio should be set to
+     * @param[in] state state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_radio_state(int state )
+    {
+        return device->set_radio_state(state );
+    }
+    /**
+     * @brief Get current radio state
+     * @param[out] state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_radio_state(int &state )
+    {
+        return device->get_radio_state(state );
+    }
+    /**
+     * @brief Set network mode
+     * @param[in] mode preferred mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_network_mode(int mode )
+    {
+        return device->set_network_mode(mode );
+    }
+    /**
+     * @brief Preferred network mode (CAT-M and/or NB-IoT)
+     * @param[in] mode preferred selection
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_preferred_mode(int mode )
+    {
+        return device->set_preferred_mode(mode );
+    }
+    /**
+     * @brief Set network bands for CAT-M or NB-IoT
+     * @param[in] mode CAT-M or NB-IoT
+     * @param[in] bands bitmap in hex representing bands
+     * @param[in] size size of teh bands bitmap
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_network_bands(const std::string &mode, const int *bands, int size )
+    {
+        return device->set_network_bands(mode, bands, size );
+    }
+    /**
+     * @brief Show network system mode
+     * @param[out] mode current network mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_network_system_mode(int &mode )
+    {
+        return device->get_network_system_mode(mode );
+    }
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result set_gnss_power_mode(int mode )
+    {
+        return device->set_gnss_power_mode(mode );
+    }
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    command_result get_gnss_power_mode(int &mode )
+    {
+        return device->get_gnss_power_mode(mode );
+    }
+};
+/**
+ * @}
+ */
+} // esp_modem
diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp
new file mode 100644
index 0000000000..7f8c1b3281
--- /dev/null
+++ b/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp
@@ -0,0 +1,394 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <memory>
+#include <utility>
+//#include "generate/esp_modem_command_declare.inc"
+#include "cxx_include/esp_modem_command_library.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+#include "esp_modem_dce_config.h"
+
+
+namespace esp_modem {
+/**
+ * @defgroup ESP_MODEM_MODULE
+ * @brief Definition of modules representing specific modem devices
+ */
+/** @addtogroup ESP_MODEM_MODULE
+* @{
+*/
+enum class command_result;
+class DTE;
+struct PdpContext;
+/**
+ * @brief This is a basic building block for custom modules as well as for the supported modules in the esp-modem component
+ * It derives from the ModuleIf.
+ */
+class GenericModule: public ModuleIf {
+public:
+    /**
+     * @brief We can construct a generic device with an existent DTE and it's configuration
+     * The configuration could be either the dce-config struct or just a pdp context
+     */
+    explicit GenericModule(std::shared_ptr<DTE> dte, std::unique_ptr<PdpContext> pdp):
+        dte(std::move(dte)), pdp(std::move(pdp)) {}
+    explicit GenericModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *config);
+    /**
+     * @brief This is a mandatory method for ModuleIf class, which sets up the device
+     * to be able to connect to the network. This typically consists of setting basic
+     * communication parameters and setting the PDP (defining logical access point
+     * to cellular network)
+     */
+    bool setup_data_mode() override
+    {
+        if (set_echo(false) != command_result::OK) {
+            return false;
+        }
+        if (set_pdp_context(*pdp) != command_result::OK) {
+            return false;
+        }
+        return true;
+    }
+    /**
+     * @brief This is a mandatory method of ModuleIf class, which defines
+     * basic commands for switching between DATA, COMMAND and CMUX modes
+     */
+    bool set_mode(modem_mode mode) override
+    {
+        if (mode == modem_mode::DATA_MODE) {
+            if (set_data_mode() != command_result::OK) {
+                return resume_data_mode() == command_result::OK;
+            }
+            return true;
+        } else if (mode == modem_mode::COMMAND_MODE) {
+            Task::Delay(1000); // Mandatory 1s pause before
+            int retry = 0;
+            while (retry++ < 3) {
+                if (set_command_mode() == command_result::OK) {
+                    return true;
+                }
+                Task::Delay(1000); // Mandatory 1s pause after escape
+                if (sync() == command_result::OK) {
+                    return true;
+                }
+                Task::Delay(1000); // Mandatory 1s pause before escape
+            }
+            return false;
+        } else if (mode == modem_mode::CMUX_MODE) {
+            return set_cmux() == command_result::OK;
+        }
+        return true;
+    }
+    /**
+     * @brief Additional method providing runtime configuration of PDP context
+     */
+    void configure_pdp_context(std::unique_ptr<PdpContext> new_pdp)
+    {
+        pdp = std::move(new_pdp);
+    }
+    /**
+     * @brief Simplified version of operator name (without the ACT, which is included in the command library)
+     */
+    command_result get_operator_name(std::string &name)
+    {
+        int dummy_act;
+        return get_operator_name(name, dummy_act);
+    }
+    /**
+     * @brief Common DCE commands generated from the API AT list
+     */
+//    DECLARE_ALL_COMMAND_APIS(virtual return_type name(...); )
+    /**
+     * @brief Sends the initial AT sequence to sync up with the device
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result sync();
+    /**
+     * @brief Reads the operator name
+     * @param[out] name operator name
+     * @param[out] act access technology
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_operator_name(std::string &name, int &act );
+    /**
+     * @brief Stores current user profile
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result store_profile();
+    /**
+     * @brief Sets the supplied PIN code
+     * @param[in] pin Pin
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_pin(const std::string &pin );
+    /**
+     * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+     * @param[in] cmd String command that's send to DTE
+     * @param[out] out Raw output from DTE
+     * @param[in] pass Pattern in response for the API to return OK
+     * @param[in] fail Pattern in response for the API to return FAIL
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout );
+    /**
+     * @brief Execute the supplied AT command
+     * @param[in] cmd AT command
+     * @param[out] out Command output string
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result at(const std::string &cmd, std::string &out, int timeout );
+    /**
+     * @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
+     */
+    virtual 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
+     */
+    virtual 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
+     */
+    virtual command_result sms_txt_mode(const bool txt );
+    /**
+     * @brief Sets the default (GSM) character set
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual 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
+     */
+    virtual command_result send_sms(const std::string &number, const std::string &message );
+    /**
+     * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result resume_data_mode();
+    /**
+     * @brief Sets php context
+     * @param[in] p1 PdP context struct to setup modem cellular connection
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_pdp_context(PdpContext &pdp );
+    /**
+     * @brief Switches to the command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_command_mode();
+    /**
+     * @brief Switches to the CMUX mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_cmux();
+    /**
+     * @brief Reads the IMSI number
+     * @param[out] imsi Module's IMSI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_imsi(std::string &imsi );
+    /**
+     * @brief Reads the IMEI number
+     * @param[out] imei Module's IMEI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_imei(std::string &imei );
+    /**
+     * @brief Reads the module name
+     * @param[out] name module name
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_module_name(std::string &name );
+    /**
+     * @brief Sets the modem to data mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual 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
+     */
+    virtual 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
+     */
+    virtual command_result set_flow_control(int dce_flow, int dte_flow );
+    /**
+     * @brief Hangs up current data call
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual 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
+     */
+    virtual command_result get_battery_status(int &voltage, int &bcs, int &bcl );
+    /**
+     * @brief Power down the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result power_down();
+    /**
+     * @brief Reset the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result reset();
+    /**
+     * @brief Configures the baudrate
+     * @param[in] baud Desired baud rate of the DTE
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_baud(int baud );
+    /**
+     * @brief Force an attempt to connect to a specific operator
+     * @param[in] mode mode of attempt
+     * mode=0 - automatic
+     * mode=1 - manual
+     * mode=2 - deregister
+     * mode=3 - set format for read operation
+     * mode=4 - manual with fallback to automatic
+     * @param[in] format what format the operator is given in
+     * format=0 - long format
+     * format=1 - short format
+     * format=2 - numeric
+     * @param[in] oper the operator to connect to
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_operator(int mode, int format, const std::string &oper );
+    /**
+     * @brief Attach or detach from the GPRS service
+     * @param[in] state 1-attach 0-detach
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_network_attachment_state(int state );
+    /**
+     * @brief Get network attachment state
+     * @param[out] state 1-attached 0-detached
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_network_attachment_state(int &state );
+    /**
+     * @brief What mode the radio should be set to
+     * @param[in] state state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_radio_state(int state );
+    /**
+     * @brief Get current radio state
+     * @param[out] state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_radio_state(int &state );
+    /**
+     * @brief Set network mode
+     * @param[in] mode preferred mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_network_mode(int mode );
+    /**
+     * @brief Preferred network mode (CAT-M and/or NB-IoT)
+     * @param[in] mode preferred selection
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_preferred_mode(int mode );
+    /**
+     * @brief Set network bands for CAT-M or NB-IoT
+     * @param[in] mode CAT-M or NB-IoT
+     * @param[in] bands bitmap in hex representing bands
+     * @param[in] size size of teh bands bitmap
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_network_bands(const std::string &mode, const int *bands, int size );
+    /**
+     * @brief Show network system mode
+     * @param[out] mode current network mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_network_system_mode(int &mode );
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result set_gnss_power_mode(int mode );
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    virtual command_result get_gnss_power_mode(int &mode );
+protected:
+    std::shared_ptr<DTE> dte; /*!< Generic device needs the DTE as a channel talk to the module using AT commands */
+    std::unique_ptr<PdpContext> pdp; /*!< It also needs a PDP data, const information used for setting up cellular network */
+};
+// Definitions of other supported modules with some specific commands overwritten
+/**
+ * @brief Specific definition of the SIM7600 module
+ */
+class SIM7600: public GenericModule {
+    using GenericModule::GenericModule;
+public:
+    command_result get_battery_status(int &voltage, int &bcs, int &bcl) override;
+    command_result power_down() override;
+    command_result set_gnss_power_mode(int mode) override;
+    command_result set_network_bands(const std::string &mode, const int *bands, int size) override;
+};
+/**
+ * @brief Specific definition of the SIM7070 module
+ */
+class SIM7070: public GenericModule {
+    using GenericModule::GenericModule;
+public:
+    command_result power_down() override;
+    command_result set_data_mode() override;
+};
+/**
+ * @brief Specific definition of the SIM7000 module
+ */
+class SIM7000: public GenericModule {
+    using GenericModule::GenericModule;
+public:
+    command_result power_down() override;
+};
+/**
+ * @brief Specific definition of the SIM800 module
+ */
+class SIM800: public GenericModule {
+    using GenericModule::GenericModule;
+public:
+    command_result power_down() override;
+};
+/**
+ * @brief Specific definition of the BG96 module
+ */
+class BG96: public GenericModule {
+    using GenericModule::GenericModule;
+public:
+    command_result set_pdp_context(PdpContext &pdp) override;
+};
+/**
+ * @}
+ */
+} // namespace esp_modem
diff --git a/components/esp_modem/command/include/esp_modem_api.h b/components/esp_modem/command/include/esp_modem_api.h
new file mode 100644
index 0000000000..0a3242169f
--- /dev/null
+++ b/components/esp_modem/command/include/esp_modem_api.h
@@ -0,0 +1,257 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_err.h"
+//#include "generate/esp_modem_command_declare.inc"
+#include "esp_modem_c_api_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Sends the initial AT sequence to sync up with the device
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_sync(esp_modem_dce_t *dce );
+/**
+ * @brief Reads the operator name
+ * @param[out] name operator name
+ * @param[out] act access technology
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce, char *name, int *act );
+/**
+ * @brief Stores current user profile
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_store_profile(esp_modem_dce_t *dce );
+/**
+ * @brief Sets the supplied PIN code
+ * @param[in] pin Pin
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce, const char *pin );
+/**
+ * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+ * @param[in] cmd String command that's send to DTE
+ * @param[out] out Raw output from DTE
+ * @param[in] pass Pattern in response for the API to return OK
+ * @param[in] fail Pattern in response for the API to return FAIL
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_at_raw(esp_modem_dce_t *dce, const char *cmd, char *out, const char *pass, const char *fail, int timeout );
+/**
+ * @brief Execute the supplied AT command
+ * @param[in] cmd AT command
+ * @param[out] out Command output string
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_at(esp_modem_dce_t *dce, const char *cmd, char *out, int timeout );
+/**
+ * @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
+ */
+esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce, bool *pin_ok );
+/**
+ * @brief Sets echo mode
+ * @param[in] echo_on true if echo mode on (repeats the commands)
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_echo(esp_modem_dce_t *dce, 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
+ */
+esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce, const bool txt );
+/**
+ * @brief Sets the default (GSM) character set
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_sms_character_set(esp_modem_dce_t *dce );
+/**
+ * @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
+ */
+esp_err_t esp_modem_send_sms(esp_modem_dce_t *dce, const char *number, const char *message );
+/**
+ * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_resume_data_mode(esp_modem_dce_t *dce );
+/**
+ * @brief Sets php context
+ * @param[in] p1 PdP context struct to setup modem cellular connection
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_pdp_context(esp_modem_dce_t *dce, esp_modem_PdpContext_t *pdp );
+/**
+ * @brief Switches to the command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_command_mode(esp_modem_dce_t *dce );
+/**
+ * @brief Switches to the CMUX mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_cmux(esp_modem_dce_t *dce );
+/**
+ * @brief Reads the IMSI number
+ * @param[out] imsi Module's IMSI number
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce, char *imsi );
+/**
+ * @brief Reads the IMEI number
+ * @param[out] imei Module's IMEI number
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_imei(esp_modem_dce_t *dce, char *imei );
+/**
+ * @brief Reads the module name
+ * @param[out] name module name
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_module_name(esp_modem_dce_t *dce, char *name );
+/**
+ * @brief Sets the modem to data mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_data_mode(esp_modem_dce_t *dce );
+/**
+ * @brief Get Signal quality
+ * @param[out] rssi signal strength indication
+ * @param[out] ber channel bit error rate
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t *dce, 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
+ */
+esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce, int dce_flow, int dte_flow );
+/**
+ * @brief Hangs up current data call
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_hang_up(esp_modem_dce_t *dce );
+/**
+ * @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
+ */
+esp_err_t esp_modem_get_battery_status(esp_modem_dce_t *dce, int *voltage, int *bcs, int *bcl );
+/**
+ * @brief Power down the module
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_power_down(esp_modem_dce_t *dce );
+/**
+ * @brief Reset the module
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_reset(esp_modem_dce_t *dce );
+/**
+ * @brief Configures the baudrate
+ * @param[in] baud Desired baud rate of the DTE
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_baud(esp_modem_dce_t *dce, int baud );
+/**
+ * @brief Force an attempt to connect to a specific operator
+ * @param[in] mode mode of attempt
+ * mode=0 - automatic
+ * mode=1 - manual
+ * mode=2 - deregister
+ * mode=3 - set format for read operation
+ * mode=4 - manual with fallback to automatic
+ * @param[in] format what format the operator is given in
+ * format=0 - long format
+ * format=1 - short format
+ * format=2 - numeric
+ * @param[in] oper the operator to connect to
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_operator(esp_modem_dce_t *dce, int mode, int format, const char *oper );
+/**
+ * @brief Attach or detach from the GPRS service
+ * @param[in] state 1-attach 0-detach
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_network_attachment_state(esp_modem_dce_t *dce, int state );
+/**
+ * @brief Get network attachment state
+ * @param[out] state 1-attached 0-detached
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_network_attachment_state(esp_modem_dce_t *dce, int *state );
+/**
+ * @brief What mode the radio should be set to
+ * @param[in] state state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_radio_state(esp_modem_dce_t *dce, int state );
+/**
+ * @brief Get current radio state
+ * @param[out] state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_radio_state(esp_modem_dce_t *dce, int *state );
+/**
+ * @brief Set network mode
+ * @param[in] mode preferred mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_network_mode(esp_modem_dce_t *dce, int mode );
+/**
+ * @brief Preferred network mode (CAT-M and/or NB-IoT)
+ * @param[in] mode preferred selection
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_preferred_mode(esp_modem_dce_t *dce, int mode );
+/**
+ * @brief Set network bands for CAT-M or NB-IoT
+ * @param[in] mode CAT-M or NB-IoT
+ * @param[in] bands bitmap in hex representing bands
+ * @param[in] size size of teh bands bitmap
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_network_bands(esp_modem_dce_t *dce, const char *mode, const int *bands, int size );
+/**
+ * @brief Show network system mode
+ * @param[out] mode current network mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_network_system_mode(esp_modem_dce_t *dce, int *mode );
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_set_gnss_power_mode(esp_modem_dce_t *dce, int mode );
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_err_t esp_modem_get_gnss_power_mode(esp_modem_dce_t *dce, int *mode );
+//  --- ESP-MODEM command module ends here ---
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/components/esp_modem/command/src/esp_modem_modules.cpp b/components/esp_modem/command/src/esp_modem_modules.cpp
new file mode 100644
index 0000000000..55454d1b8b
--- /dev/null
+++ b/components/esp_modem/command/src/esp_modem_modules.cpp
@@ -0,0 +1,406 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "cxx_include/esp_modem_api.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_dte.hpp"
+
+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)) {}
+/**
+ * @brief Sends the initial AT sequence to sync up with the device
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::sync()
+{
+    return esp_modem::dce_commands::sync(dte.get() );
+}
+/**
+ * @brief Reads the operator name
+ * @param[out] name operator name
+ * @param[out] act access technology
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_operator_name(std::string &name, int &act )
+{
+    return esp_modem::dce_commands::get_operator_name(dte.get(), name, act );
+}
+/**
+ * @brief Stores current user profile
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::store_profile()
+{
+    return esp_modem::dce_commands::store_profile(dte.get() );
+}
+/**
+ * @brief Sets the supplied PIN code
+ * @param[in] pin Pin
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_pin(const std::string &pin )
+{
+    return esp_modem::dce_commands::set_pin(dte.get(), pin );
+}
+/**
+ * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+ * @param[in] cmd String command that's send to DTE
+ * @param[out] out Raw output from DTE
+ * @param[in] pass Pattern in response for the API to return OK
+ * @param[in] fail Pattern in response for the API to return FAIL
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout )
+{
+    return esp_modem::dce_commands::at_raw(dte.get(), cmd, out, pass, fail, timeout );
+}
+/**
+ * @brief Execute the supplied AT command
+ * @param[in] cmd AT command
+ * @param[out] out Command output string
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::at(const std::string &cmd, std::string &out, int timeout )
+{
+    return esp_modem::dce_commands::at(dte.get(), cmd, out, timeout );
+}
+/**
+ * @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 GenericModule::read_pin(bool &pin_ok )
+{
+    return esp_modem::dce_commands::read_pin(dte.get(), 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 GenericModule::set_echo(const bool echo_on )
+{
+    return esp_modem::dce_commands::set_echo(dte.get(), 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 GenericModule::sms_txt_mode(const bool txt )
+{
+    return esp_modem::dce_commands::sms_txt_mode(dte.get(), txt );
+}
+/**
+ * @brief Sets the default (GSM) character set
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::sms_character_set()
+{
+    return esp_modem::dce_commands::sms_character_set(dte.get() );
+}
+/**
+ * @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 GenericModule::send_sms(const std::string &number, const std::string &message )
+{
+    return esp_modem::dce_commands::send_sms(dte.get(), number, message );
+}
+/**
+ * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::resume_data_mode()
+{
+    return esp_modem::dce_commands::resume_data_mode(dte.get() );
+}
+/**
+ * @brief Sets php context
+ * @param[in] p1 PdP context struct to setup modem cellular connection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_pdp_context(PdpContext &pdp )
+{
+    return esp_modem::dce_commands::set_pdp_context(dte.get(), pdp );
+}
+/**
+ * @brief Switches to the command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_command_mode()
+{
+    return esp_modem::dce_commands::set_command_mode(dte.get() );
+}
+/**
+ * @brief Switches to the CMUX mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_cmux()
+{
+    return esp_modem::dce_commands::set_cmux(dte.get() );
+}
+/**
+ * @brief Reads the IMSI number
+ * @param[out] imsi Module's IMSI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_imsi(std::string &imsi )
+{
+    return esp_modem::dce_commands::get_imsi(dte.get(), imsi );
+}
+/**
+ * @brief Reads the IMEI number
+ * @param[out] imei Module's IMEI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_imei(std::string &imei )
+{
+    return esp_modem::dce_commands::get_imei(dte.get(), imei );
+}
+/**
+ * @brief Reads the module name
+ * @param[out] name module name
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_module_name(std::string &name )
+{
+    return esp_modem::dce_commands::get_module_name(dte.get(), name );
+}
+/**
+ * @brief Sets the modem to data mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_data_mode()
+{
+    return esp_modem::dce_commands::set_data_mode(dte.get() );
+}
+/**
+ * @brief Get Signal quality
+ * @param[out] rssi signal strength indication
+ * @param[out] ber channel bit error rate
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_signal_quality(int &rssi, int &ber )
+{
+    return esp_modem::dce_commands::get_signal_quality(dte.get(), rssi, 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 GenericModule::set_flow_control(int dce_flow, int dte_flow )
+{
+    return esp_modem::dce_commands::set_flow_control(dte.get(), dce_flow, dte_flow );
+}
+/**
+ * @brief Hangs up current data call
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::hang_up()
+{
+    return esp_modem::dce_commands::hang_up(dte.get() );
+}
+/**
+ * @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 GenericModule::get_battery_status(int &voltage, int &bcs, int &bcl )
+{
+    return esp_modem::dce_commands::get_battery_status(dte.get(), voltage, bcs, bcl );
+}
+/**
+ * @brief Power down the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::power_down()
+{
+    return esp_modem::dce_commands::power_down(dte.get() );
+}
+/**
+ * @brief Reset the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::reset()
+{
+    return esp_modem::dce_commands::reset(dte.get() );
+}
+/**
+ * @brief Configures the baudrate
+ * @param[in] baud Desired baud rate of the DTE
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_baud(int baud )
+{
+    return esp_modem::dce_commands::set_baud(dte.get(), baud );
+}
+/**
+ * @brief Force an attempt to connect to a specific operator
+ * @param[in] mode mode of attempt
+ * mode=0 - automatic
+ * mode=1 - manual
+ * mode=2 - deregister
+ * mode=3 - set format for read operation
+ * mode=4 - manual with fallback to automatic
+ * @param[in] format what format the operator is given in
+ * format=0 - long format
+ * format=1 - short format
+ * format=2 - numeric
+ * @param[in] oper the operator to connect to
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_operator(int mode, int format, const std::string &oper )
+{
+    return esp_modem::dce_commands::set_operator(dte.get(), mode, format, oper );
+}
+/**
+ * @brief Attach or detach from the GPRS service
+ * @param[in] state 1-attach 0-detach
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_network_attachment_state(int state )
+{
+    return esp_modem::dce_commands::set_network_attachment_state(dte.get(), state );
+}
+/**
+ * @brief Get network attachment state
+ * @param[out] state 1-attached 0-detached
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_network_attachment_state(int &state )
+{
+    return esp_modem::dce_commands::get_network_attachment_state(dte.get(), state );
+}
+/**
+ * @brief What mode the radio should be set to
+ * @param[in] state state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_radio_state(int state )
+{
+    return esp_modem::dce_commands::set_radio_state(dte.get(), state );
+}
+/**
+ * @brief Get current radio state
+ * @param[out] state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_radio_state(int &state )
+{
+    return esp_modem::dce_commands::get_radio_state(dte.get(), state );
+}
+/**
+ * @brief Set network mode
+ * @param[in] mode preferred mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_network_mode(int mode )
+{
+    return esp_modem::dce_commands::set_network_mode(dte.get(), mode );
+}
+/**
+ * @brief Preferred network mode (CAT-M and/or NB-IoT)
+ * @param[in] mode preferred selection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_preferred_mode(int mode )
+{
+    return esp_modem::dce_commands::set_preferred_mode(dte.get(), mode );
+}
+/**
+ * @brief Set network bands for CAT-M or NB-IoT
+ * @param[in] mode CAT-M or NB-IoT
+ * @param[in] bands bitmap in hex representing bands
+ * @param[in] size size of teh bands bitmap
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_network_bands(const std::string &mode, const int *bands, int size )
+{
+    return esp_modem::dce_commands::set_network_bands(dte.get(), mode, bands, size );
+}
+/**
+ * @brief Show network system mode
+ * @param[out] mode current network mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_network_system_mode(int &mode )
+{
+    return esp_modem::dce_commands::get_network_system_mode(dte.get(), mode );
+}
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::set_gnss_power_mode(int mode )
+{
+    return esp_modem::dce_commands::set_gnss_power_mode(dte.get(), mode );
+}
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result GenericModule::get_gnss_power_mode(int &mode )
+{
+    return esp_modem::dce_commands::get_gnss_power_mode(dte.get(), mode );
+}
+// Usage examples:
+// Zero arguments
+// Helper to apply the correct macro to each parameter
+//
+// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
+//
+//
+// Handle specific commands for specific supported modems
+//
+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::set_network_bands(const std::string &mode, const int *bands, int size)
+{
+    return dce_commands::set_network_bands_sim76xx(dte.get(), mode, bands, size);
+}
+command_result SIM7600::set_gnss_power_mode(int mode)
+{
+    return dce_commands::set_gnss_power_mode_sim76xx(dte.get(), mode);
+}
+command_result SIM7600::power_down()
+{
+    return dce_commands::power_down_sim76xx(dte.get());
+}
+command_result SIM7070::power_down()
+{
+    return dce_commands::power_down_sim70xx(dte.get());
+}
+command_result SIM7070::set_data_mode()
+{
+    return dce_commands::set_data_mode_alt(dte.get());
+}
+command_result SIM7000::power_down()
+{
+    return dce_commands::power_down_sim70xx(dte.get());
+}
+command_result SIM800::power_down()
+{
+    return dce_commands::power_down_sim8xx(dte.get());
+}
+command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp)
+{
+    return dce_commands::set_pdp_context(dte.get(), pdp, 300);
+}
+}
diff --git a/components/esp_modem/examples/modem_console/main/CMakeLists.txt b/components/esp_modem/examples/modem_console/main/CMakeLists.txt
index 2465c0aec4..67de0841f8 100644
--- a/components/esp_modem/examples/modem_console/main/CMakeLists.txt
+++ b/components/esp_modem/examples/modem_console/main/CMakeLists.txt
@@ -1,7 +1,13 @@
+if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
+    set(command_dir "generate")
+else()
+    set(command_dir "command")
+endif()
+
 idf_component_register(SRCS "modem_console_main.cpp"
                             "console_helper.cpp"
-                            "my_module_dce.cpp"
+                            "${command_dir}/my_module_dce.cpp"
                             "httpget_handle.c"
                             "ping_handle.c"
                        REQUIRES console esp_http_client nvs_flash
-                       INCLUDE_DIRS ".")
+                       INCLUDE_DIRS "." "${command_dir}")
diff --git a/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp b/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp
new file mode 100644
index 0000000000..069e8b0228
--- /dev/null
+++ b/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp
@@ -0,0 +1,436 @@
+/*
+ * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+/* Modem console example: Custom DCE
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <cstring>
+#include "cxx_include/esp_modem_api.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+//#include "generate/esp_modem_command_declare.inc"
+#include "my_module_dce.hpp"
+
+using namespace esp_modem;
+//
+// Define preprocessor's forwarding to dce_commands definitions
+//
+//
+// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
+//
+//DECLARE_ALL_COMMAND_APIS(return_type name(...) )
+/**
+ * @brief Sends the initial AT sequence to sync up with the device
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::sync()
+{
+    return esp_modem::dce_commands::sync(this );
+}
+/**
+ * @brief Reads the operator name
+ * @param[out] name operator name
+ * @param[out] act access technology
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_operator_name(std::string &name, int &act )
+{
+    return esp_modem::dce_commands::get_operator_name(this, name, act );
+}
+/**
+ * @brief Stores current user profile
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::store_profile()
+{
+    return esp_modem::dce_commands::store_profile(this );
+}
+/**
+ * @brief Sets the supplied PIN code
+ * @param[in] pin Pin
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_pin(const std::string &pin )
+{
+    return esp_modem::dce_commands::set_pin(this, pin );
+}
+/**
+ * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+ * @param[in] cmd String command that's send to DTE
+ * @param[out] out Raw output from DTE
+ * @param[in] pass Pattern in response for the API to return OK
+ * @param[in] fail Pattern in response for the API to return FAIL
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout )
+{
+    return esp_modem::dce_commands::at_raw(this, cmd, out, pass, fail, timeout );
+}
+/**
+ * @brief Execute the supplied AT command
+ * @param[in] cmd AT command
+ * @param[out] out Command output string
+ * @param[in] timeout AT command timeout in milliseconds
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::at(const std::string &cmd, std::string &out, int timeout )
+{
+    return esp_modem::dce_commands::at(this, cmd, out, timeout );
+}
+/**
+ * @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 Shiny::DCE::read_pin(bool &pin_ok )
+{
+    return esp_modem::dce_commands::read_pin(this, 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 Shiny::DCE::set_echo(const bool echo_on )
+{
+    return esp_modem::dce_commands::set_echo(this, 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 Shiny::DCE::sms_txt_mode(const bool txt )
+{
+    return esp_modem::dce_commands::sms_txt_mode(this, txt );
+}
+/**
+ * @brief Sets the default (GSM) character set
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::sms_character_set()
+{
+    return esp_modem::dce_commands::sms_character_set(this );
+}
+/**
+ * @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 Shiny::DCE::send_sms(const std::string &number, const std::string &message )
+{
+    return esp_modem::dce_commands::send_sms(this, number, message );
+}
+/**
+ * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::resume_data_mode()
+{
+    return esp_modem::dce_commands::resume_data_mode(this );
+}
+/**
+ * @brief Sets php context
+ * @param[in] p1 PdP context struct to setup modem cellular connection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_pdp_context(PdpContext &pdp )
+{
+    return esp_modem::dce_commands::set_pdp_context(this, pdp );
+}
+/**
+ * @brief Switches to the command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_command_mode()
+{
+    return esp_modem::dce_commands::set_command_mode(this );
+}
+/**
+ * @brief Switches to the CMUX mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_cmux()
+{
+    return esp_modem::dce_commands::set_cmux(this );
+}
+/**
+ * @brief Reads the IMSI number
+ * @param[out] imsi Module's IMSI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_imsi(std::string &imsi )
+{
+    return esp_modem::dce_commands::get_imsi(this, imsi );
+}
+/**
+ * @brief Reads the IMEI number
+ * @param[out] imei Module's IMEI number
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_imei(std::string &imei )
+{
+    return esp_modem::dce_commands::get_imei(this, imei );
+}
+/**
+ * @brief Reads the module name
+ * @param[out] name module name
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_module_name(std::string &name )
+{
+    return esp_modem::dce_commands::get_module_name(this, name );
+}
+/**
+ * @brief Sets the modem to data mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_data_mode()
+{
+    return esp_modem::dce_commands::set_data_mode(this );
+}
+/**
+ * @brief Get Signal quality
+ * @param[out] rssi signal strength indication
+ * @param[out] ber channel bit error rate
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_signal_quality(int &rssi, int &ber )
+{
+    return esp_modem::dce_commands::get_signal_quality(this, rssi, 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 Shiny::DCE::set_flow_control(int dce_flow, int dte_flow )
+{
+    return esp_modem::dce_commands::set_flow_control(this, dce_flow, dte_flow );
+}
+/**
+ * @brief Hangs up current data call
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::hang_up()
+{
+    return esp_modem::dce_commands::hang_up(this );
+}
+/**
+ * @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 Shiny::DCE::get_battery_status(int &voltage, int &bcs, int &bcl )
+{
+    return esp_modem::dce_commands::get_battery_status(this, voltage, bcs, bcl );
+}
+/**
+ * @brief Power down the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::power_down()
+{
+    return esp_modem::dce_commands::power_down(this );
+}
+/**
+ * @brief Reset the module
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::reset()
+{
+    return esp_modem::dce_commands::reset(this );
+}
+/**
+ * @brief Configures the baudrate
+ * @param[in] baud Desired baud rate of the DTE
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_baud(int baud )
+{
+    return esp_modem::dce_commands::set_baud(this, baud );
+}
+/**
+ * @brief Force an attempt to connect to a specific operator
+ * @param[in] mode mode of attempt
+ * mode=0 - automatic
+ * mode=1 - manual
+ * mode=2 - deregister
+ * mode=3 - set format for read operation
+ * mode=4 - manual with fallback to automatic
+ * @param[in] format what format the operator is given in
+ * format=0 - long format
+ * format=1 - short format
+ * format=2 - numeric
+ * @param[in] oper the operator to connect to
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_operator(int mode, int format, const std::string &oper )
+{
+    return esp_modem::dce_commands::set_operator(this, mode, format, oper );
+}
+/**
+ * @brief Attach or detach from the GPRS service
+ * @param[in] state 1-attach 0-detach
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_network_attachment_state(int state )
+{
+    return esp_modem::dce_commands::set_network_attachment_state(this, state );
+}
+/**
+ * @brief Get network attachment state
+ * @param[out] state 1-attached 0-detached
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_network_attachment_state(int &state )
+{
+    return esp_modem::dce_commands::get_network_attachment_state(this, state );
+}
+/**
+ * @brief What mode the radio should be set to
+ * @param[in] state state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_radio_state(int state )
+{
+    return esp_modem::dce_commands::set_radio_state(this, state );
+}
+/**
+ * @brief Get current radio state
+ * @param[out] state 1-full 0-minimum ...
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_radio_state(int &state )
+{
+    return esp_modem::dce_commands::get_radio_state(this, state );
+}
+/**
+ * @brief Set network mode
+ * @param[in] mode preferred mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_network_mode(int mode )
+{
+    return esp_modem::dce_commands::set_network_mode(this, mode );
+}
+/**
+ * @brief Preferred network mode (CAT-M and/or NB-IoT)
+ * @param[in] mode preferred selection
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_preferred_mode(int mode )
+{
+    return esp_modem::dce_commands::set_preferred_mode(this, mode );
+}
+/**
+ * @brief Set network bands for CAT-M or NB-IoT
+ * @param[in] mode CAT-M or NB-IoT
+ * @param[in] bands bitmap in hex representing bands
+ * @param[in] size size of teh bands bitmap
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_network_bands(const std::string &mode, const int *bands, int size )
+{
+    return esp_modem::dce_commands::set_network_bands(this, mode, bands, size );
+}
+/**
+ * @brief Show network system mode
+ * @param[out] mode current network mode
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_network_system_mode(int &mode )
+{
+    return esp_modem::dce_commands::get_network_system_mode(this, mode );
+}
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::set_gnss_power_mode(int mode )
+{
+    return esp_modem::dce_commands::set_gnss_power_mode(this, mode );
+}
+/**
+ * @brief GNSS power control
+ * @param[out] mode power mode (0 - off, 1 - on)
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::get_gnss_power_mode(int &mode )
+{
+    return esp_modem::dce_commands::get_gnss_power_mode(this, mode );
+}
+std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
+        std::shared_ptr<esp_modem::DTE> dte,
+        esp_netif_t *netif)
+{
+    return Shiny::Factory::create(config, std::move(dte), netif);
+}
+/**
+ * @brief Definition of the command API, which makes the Shiny::DCE "command-able class"
+ * @param cmd Command to send
+ * @param got_line Recv line callback
+ * @param time_ms timeout in ms
+ * @param separator line break separator
+ * @return OK, FAIL or TIMEOUT
+ */
+command_result Shiny::DCE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator)
+{
+    if (!handling_urc) {
+        return dte->command(cmd, got_line, time_ms, separator);
+    }
+    handle_cmd = got_line;
+    signal.clear((1) | (2));
+    esp_modem::DTE_Command command{cmd};
+    dte->write(command);
+    signal.wait_any((1) | (2), time_ms);
+    handle_cmd = nullptr;
+    if (signal.is_any((1))) {
+        return esp_modem::command_result::OK;
+    }
+    if (signal.is_any((2))) {
+        return esp_modem::command_result::FAIL;
+    }
+    return esp_modem::command_result::TIMEOUT;
+}
+/**
+ * @brief Handle received data
+ *
+ * @param data Data received from the device
+ * @param len Length of the data
+ * @return standard command return code (OK|FAIL|TIMEOUT)
+ */
+command_result Shiny::DCE::handle_data(uint8_t *data, size_t len)
+{
+    if (std::memchr(data, '\n', len)) {
+        if (handle_urc) {
+            handle_urc(data, len);
+        }
+        if (handle_cmd) {
+            auto ret = handle_cmd(data, len);
+            if (ret == esp_modem::command_result::TIMEOUT) {
+                return command_result::TIMEOUT;
+            }
+            if (ret == esp_modem::command_result::OK) {
+                signal.set((1));
+            }
+            if (ret == esp_modem::command_result::FAIL) {
+                signal.set((2));
+            }
+        }
+    }
+    return command_result::TIMEOUT;
+}
diff --git a/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp b/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp
new file mode 100644
index 0000000000..91ee34852d
--- /dev/null
+++ b/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp
@@ -0,0 +1,322 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+/*
+ * Modem console example: Custom DCE
+*/
+
+#pragma once
+
+
+#include <utility>
+
+#include "cxx_include/esp_modem_dce_factory.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+
+/**
+ * @brief Definition of a custom DCE uses GenericModule and all its methods
+ * but could override command processing. Here, for demonstration purposes only,
+ * we "inject" URC handler to the actual command processing.
+ * This is possible since we inherit from `CommandableIf` and redefine `command()` method.
+ * Then we're able to use declare all common methods from the command library
+ * to be processed using "our" `command()` method (with custom URC handler).
+ */
+namespace Shiny {
+using namespace esp_modem;
+class DCE : public esp_modem::DCE_T<GenericModule>, public CommandableIf {
+public:
+    using DCE_T<GenericModule>::DCE_T;
+    command_result
+    command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
+    {
+        return command(cmd, got_line, time_ms, '\n');
+    }
+    command_result
+    command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator) override;
+    int write(uint8_t *data, size_t len) override
+    {
+        return dte->write(data, len);
+    }
+    void on_read(got_line_cb on_data) override
+    {
+        return dte->on_read(on_data);
+    }
+//    DECLARE_ALL_COMMAND_APIS(forwards name(...))
+    /**
+     * @brief Sends the initial AT sequence to sync up with the device
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result sync();
+    /**
+     * @brief Reads the operator name
+     * @param[out] name operator name
+     * @param[out] act access technology
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_operator_name(std::string &name, int &act );
+    /**
+     * @brief Stores current user profile
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result store_profile();
+    /**
+     * @brief Sets the supplied PIN code
+     * @param[in] pin Pin
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_pin(const std::string &pin );
+    /**
+     * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+     * @param[in] cmd String command that's send to DTE
+     * @param[out] out Raw output from DTE
+     * @param[in] pass Pattern in response for the API to return OK
+     * @param[in] fail Pattern in response for the API to return FAIL
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout );
+    /**
+     * @brief Execute the supplied AT command
+     * @param[in] cmd AT command
+     * @param[out] out Command output string
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result at(const std::string &cmd, std::string &out, int timeout );
+    /**
+     * @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
+     */
+    esp_modem::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
+     */
+    esp_modem::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
+     */
+    esp_modem::command_result sms_txt_mode(const bool txt );
+    /**
+     * @brief Sets the default (GSM) character set
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::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
+     */
+    esp_modem::command_result send_sms(const std::string &number, const std::string &message );
+    /**
+     * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result resume_data_mode();
+    /**
+     * @brief Sets php context
+     * @param[in] p1 PdP context struct to setup modem cellular connection
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_pdp_context(PdpContext &pdp );
+    /**
+     * @brief Switches to the command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_command_mode();
+    /**
+     * @brief Switches to the CMUX mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_cmux();
+    /**
+     * @brief Reads the IMSI number
+     * @param[out] imsi Module's IMSI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_imsi(std::string &imsi );
+    /**
+     * @brief Reads the IMEI number
+     * @param[out] imei Module's IMEI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_imei(std::string &imei );
+    /**
+     * @brief Reads the module name
+     * @param[out] name module name
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_module_name(std::string &name );
+    /**
+     * @brief Sets the modem to data mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::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
+     */
+    esp_modem::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
+     */
+    esp_modem::command_result set_flow_control(int dce_flow, int dte_flow );
+    /**
+     * @brief Hangs up current data call
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::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
+     */
+    esp_modem::command_result get_battery_status(int &voltage, int &bcs, int &bcl );
+    /**
+     * @brief Power down the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result power_down();
+    /**
+     * @brief Reset the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result reset();
+    /**
+     * @brief Configures the baudrate
+     * @param[in] baud Desired baud rate of the DTE
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_baud(int baud );
+    /**
+     * @brief Force an attempt to connect to a specific operator
+     * @param[in] mode mode of attempt
+     * mode=0 - automatic
+     * mode=1 - manual
+     * mode=2 - deregister
+     * mode=3 - set format for read operation
+     * mode=4 - manual with fallback to automatic
+     * @param[in] format what format the operator is given in
+     * format=0 - long format
+     * format=1 - short format
+     * format=2 - numeric
+     * @param[in] oper the operator to connect to
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_operator(int mode, int format, const std::string &oper );
+    /**
+     * @brief Attach or detach from the GPRS service
+     * @param[in] state 1-attach 0-detach
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_attachment_state(int state );
+    /**
+     * @brief Get network attachment state
+     * @param[out] state 1-attached 0-detached
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_network_attachment_state(int &state );
+    /**
+     * @brief What mode the radio should be set to
+     * @param[in] state state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_radio_state(int state );
+    /**
+     * @brief Get current radio state
+     * @param[out] state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_radio_state(int &state );
+    /**
+     * @brief Set network mode
+     * @param[in] mode preferred mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_mode(int mode );
+    /**
+     * @brief Preferred network mode (CAT-M and/or NB-IoT)
+     * @param[in] mode preferred selection
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_preferred_mode(int mode );
+    /**
+     * @brief Set network bands for CAT-M or NB-IoT
+     * @param[in] mode CAT-M or NB-IoT
+     * @param[in] bands bitmap in hex representing bands
+     * @param[in] size size of teh bands bitmap
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_bands(const std::string &mode, const int *bands, int size );
+    /**
+     * @brief Show network system mode
+     * @param[out] mode current network mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_network_system_mode(int &mode );
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_gnss_power_mode(int mode );
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_gnss_power_mode(int &mode );
+    void set_on_read(esp_modem::got_line_cb on_read_cb)
+    {
+        if (on_read_cb == nullptr) {
+            handling_urc = false;
+            handle_urc = nullptr;
+            dte->on_read(nullptr);
+            return;
+        }
+        handle_urc = std::move(on_read_cb);
+        dte->on_read([this](uint8_t *data, size_t len) {
+            this->handle_data(data, len);
+            return command_result::TIMEOUT;
+        });
+        handling_urc = true;
+    }
+private:
+    got_line_cb handle_urc{nullptr};
+    got_line_cb handle_cmd{nullptr};
+    SignalGroup signal;
+    bool handling_urc {false};
+    command_result handle_data(uint8_t *data, size_t len);
+};
+class Factory: public ::esp_modem::dce_factory::Factory {
+public:
+    static std::unique_ptr<DCE> create(const esp_modem::dce_config *config,
+                                       std::shared_ptr<esp_modem::DTE> dte,
+                                       esp_netif_t *netif)
+    {
+        return build_generic_DCE<GenericModule, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
+    }
+};
+} // namespace Shiny
+/**
+ * @brief Helper create method which employs the DCE factory for creating DCE objects templated by a custom module
+ * @return unique pointer of the resultant DCE
+ */
+std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
+        std::shared_ptr<esp_modem::DTE> dte,
+        esp_netif_t *netif);
diff --git a/components/esp_modem/examples/modem_console/main/my_module_dce.cpp b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp
similarity index 80%
rename from components/esp_modem/examples/modem_console/main/my_module_dce.cpp
rename to components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp
index 3d35722b5f..09287fce47 100644
--- a/components/esp_modem/examples/modem_console/main/my_module_dce.cpp
+++ b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Unlicense OR CC0-1.0
  */
@@ -15,37 +15,28 @@
 #include <cstring>
 #include "cxx_include/esp_modem_api.hpp"
 #include "cxx_include/esp_modem_dce_module.hpp"
-#include "generate/esp_modem_command_declare.inc"
+//#include "generate/esp_modem_command_declare.inc"
 #include "my_module_dce.hpp"
 
+//  --- ESP-MODEM command module starts here ---
 using namespace esp_modem;
 
 //
 // Define preprocessor's forwarding to dce_commands definitions
 //
 
-// Helper macros to handle multiple arguments of declared API
-#define ARGS0
-#define ARGS1 , p1
-#define ARGS2 , p1 , p2
-#define ARGS3 , p1 , p2 , p3
-#define ARGS4 , p1 , p2 , p3, p4
-#define ARGS5 , p1 , p2 , p3, p4, p5
-#define ARGS6 , p1 , p2 , p3, p4, p5, p6
-
-#define _ARGS(x)  ARGS ## x
-#define ARGS(x)  _ARGS(x)
-
 #define CMD_OK    (1)
 #define CMD_FAIL  (2)
 
 //
 // 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 Shiny::DCE::name(__VA_ARGS__) { return esp_modem::dce_commands::name(this ARGS(arg_nr) ); }
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+     return_type Shiny::DCE::name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__)) { return esp_modem::dce_commands::name(this ESP_MODEM_COMMAND_FORWARD_AFTER(__VA_ARGS__) ); }
 
-DECLARE_ALL_COMMAND_APIS(return_type name(...) )
+//DECLARE_ALL_COMMAND_APIS(return_type name(...) )
+#include "esp_modem_command_declare.inc"
 
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
diff --git a/components/esp_modem/examples/modem_console/main/my_module_dce.hpp b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp
similarity index 88%
rename from components/esp_modem/examples/modem_console/main/my_module_dce.hpp
rename to components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp
index fe9fb4450e..10467d6c8d 100644
--- a/components/esp_modem/examples/modem_console/main/my_module_dce.hpp
+++ b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp
@@ -16,6 +16,7 @@
 #include "cxx_include/esp_modem_dce_factory.hpp"
 #include "cxx_include/esp_modem_dce_module.hpp"
 
+//  --- ESP-MODEM command module starts here ---
 /**
  * @brief Definition of a custom DCE uses GenericModule and all its methods
  * but could override command processing. Here, for demonstration purposes only,
@@ -51,10 +52,12 @@ class DCE : public esp_modem::DCE_T<GenericModule>, public CommandableIf {
         return dte->on_read(on_data);
     }
 
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-    esp_modem::return_type name(__VA_ARGS__);
+#include "../generate/include/esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+    esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
 
-    DECLARE_ALL_COMMAND_APIS(forwards name(...))
+//    DECLARE_ALL_COMMAND_APIS(forwards name(...))
+#include "../generate/include/esp_modem_command_declare.inc"
 
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
diff --git a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp
index 42dd6a76e4..f5ad648141 100644
--- a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp
+++ b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp
@@ -362,7 +362,8 @@ extern "C" void app_main(void)
     });
     const ConsoleCommand GetBatteryStatus("get_battery_status", "Reads voltage/battery status", no_args, [&](ConsoleCommand * c) {
         int volt, bcl, bcs;
-        CHECK_ERR(dce->get_battery_status(volt, bcl, bcs), ESP_LOGI(TAG, "OK. volt=%d, bcl=%d, bcs=%d", volt, bcl, bcs));
+        CHECK_ERR(dce->get_module()->get_battery_status(volt, bcl, bcs), ESP_LOGI(TAG, "OK. volt=%d, bcl=%d, bcs=%d", volt, bcl, bcs));
+        dce->get_battery_status(volt, bcl, bcs);
     });
     const ConsoleCommand PowerDown("power_down", "power down the module", no_args, [&](ConsoleCommand * c) {
         ESP_LOGI(TAG, "Power down the module...");
diff --git a/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt b/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt
index 4ecf6249f9..3788c86081 100644
--- a/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt
+++ b/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt
@@ -4,8 +4,14 @@ elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
     set(device_srcs sock_commands_sim7600.cpp)
 endif()
 
+if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
+    set(command_dir "generate")
+else()
+    set(command_dir "command")
+endif()
+
 idf_component_register(SRCS "modem_client.cpp"
-                            "sock_dce.cpp"
+                            "${command_dir}/sock_dce.cpp"
                             "tcp_transport_at.cpp"
                             "${device_srcs}"
-                       INCLUDE_DIRS ".")
+                       INCLUDE_DIRS "." "${command_dir}")
diff --git a/components/esp_modem/examples/modem_tcp_client/main/command/sock_commands.hpp b/components/esp_modem/examples/modem_tcp_client/main/command/sock_commands.hpp
new file mode 100644
index 0000000000..e61022d76e
--- /dev/null
+++ b/components/esp_modem/examples/modem_tcp_client/main/command/sock_commands.hpp
@@ -0,0 +1,51 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "cxx_include/esp_modem_dte.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+
+
+namespace sock_commands {
+
+/**
+ * @brief Opens network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result net_open(esp_modem::CommandableIf *t );
+/**
+ * @brief Closes network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result net_close(esp_modem::CommandableIf *t );
+/**
+ * @brief Opens a TCP connection
+ * @param[in] host Host name or IP address to connect to
+ * @param[in] port Port number
+ * @param[in] timeout Connection timeout
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result tcp_open(esp_modem::CommandableIf *t, const std::string &host, int port, int timeout );
+/**
+ * @brief Closes opened TCP socket
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result tcp_close(esp_modem::CommandableIf *t );
+/**
+ * @brief Gets modem IP address
+ * @param[out] addr String representation of modem's IP
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result get_ip(esp_modem::CommandableIf *t, std::string &addr );
+/**
+ * @brief Sets Rx mode
+ * @param[in] mode 0=auto, 1=manual
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result set_rx_mode(esp_modem::CommandableIf *t, int mode );
+}
diff --git a/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.cpp b/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.cpp
new file mode 100644
index 0000000000..6a1f2bf23c
--- /dev/null
+++ b/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.cpp
@@ -0,0 +1,361 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <charconv>
+#include <sys/socket.h>
+#include "esp_vfs.h"
+#include "esp_vfs_eventfd.h"
+
+#include "sock_dce.hpp"
+
+namespace sock_dce {
+
+constexpr auto const *TAG = "sock_dce";
+
+
+bool DCE::perform_sock()
+{
+    if (listen_sock == -1) {
+        ESP_LOGE(TAG, "Listening socket not ready");
+        close_sock();
+        return false;
+    }
+    if (sock == -1) {   // no active socket, need to accept one first
+        return accept_sock();
+    }
+
+    // we have a socket, let's check the status
+    struct timeval tv = {
+        .tv_sec = 0,
+        .tv_usec = 500000,
+    };
+    if (state == status::PENDING) {
+        vTaskDelay(pdMS_TO_TICKS(500));
+        state = at.pending();
+        return true;
+    }
+    fd_set fdset;
+    FD_ZERO(&fdset);
+    FD_SET(sock, &fdset);
+    FD_SET(data_ready_fd, &fdset);
+    int s = select(std::max(sock, data_ready_fd) + 1, &fdset, nullptr, nullptr, &tv);
+    if (s == 0) {
+        ESP_LOGV(TAG, "perform select timeout...");
+        return true;
+    } else if (s < 0) {
+        ESP_LOGE(TAG,  "select error %d", errno);
+        close_sock();
+        return false;
+    }
+    if (FD_ISSET(sock, &fdset) && !sock_to_at()) {
+        return false;
+    }
+    if (FD_ISSET(data_ready_fd, &fdset) && !at_to_sock()) {
+        return false;
+    }
+    return true;
+}
+
+void DCE::perform_at(uint8_t *data, size_t len)
+{
+    ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
+    switch (at.process_data(state, data, len)) {
+    case Responder::ret::OK:
+        state = status::IDLE;
+        signal.set(IDLE);
+        return;
+    case Responder::ret::FAIL:
+        state = status::FAILED;
+        signal.set(IDLE);
+        return;
+    case Responder::ret::NEED_MORE_DATA:
+        return;
+    case Responder::ret::IN_PROGRESS:
+        break;
+    case Responder::ret::NEED_MORE_TIME:
+        state = status::PENDING;
+        return;
+    }
+    std::string_view response((char *)data, len);
+    switch (at.check_async_replies(state, response)) {
+    case Responder::ret::OK:
+        state = status::IDLE;
+        signal.set(IDLE);
+        return;
+    case Responder::ret::FAIL:
+        state = status::FAILED;
+        signal.set(IDLE);
+        return;
+    case Responder::ret::NEED_MORE_TIME:
+        state = status::PENDING;
+        return;
+    case Responder::ret::NEED_MORE_DATA:
+    case Responder::ret::IN_PROGRESS:
+        break;
+    }
+}
+
+void DCE::close_sock()
+{
+    if (sock > 0) {
+        close(sock);
+        sock = -1;
+    }
+    dte->on_read(nullptr);
+    const int retries = 5;
+    int i = 0;
+    while (net_close() != esp_modem::command_result::OK) {
+        if (i++ > retries) {
+            ESP_LOGE(TAG, "Failed to close network");
+            return;
+        }
+        esp_modem::Task::Delay(1000);
+    }
+}
+
+bool DCE::at_to_sock()
+{
+    uint64_t data;
+    read(data_ready_fd, &data, sizeof(data));
+    ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
+    if (!signal.wait(IDLE, 1000)) {
+        ESP_LOGE(TAG, "Failed to get idle");
+        close_sock();
+        return false;
+    }
+    if (state != status::IDLE) {
+        ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
+        close_sock();
+        return false;
+    }
+    state = status::RECEIVING;
+    at.start_receiving(at.get_buf_len());
+    return true;
+}
+
+bool DCE::sock_to_at()
+{
+    ESP_LOGD(TAG,  "socket read: data available");
+    if (!signal.wait(IDLE, 1000)) {
+        ESP_LOGE(TAG,  "Failed to get idle");
+        close_sock();
+        return false;
+    }
+    if (state != status::IDLE) {
+        ESP_LOGE(TAG,  "Unexpected state %d", static_cast<int>(state));
+        close_sock();
+        return false;
+    }
+    state = status::SENDING;
+    int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
+    if (len < 0) {
+        ESP_LOGE(TAG,  "read error %d", errno);
+        close_sock();
+        return false;
+    } else if (len == 0) {
+        ESP_LOGE(TAG,  "EOF %d", errno);
+        close_sock();
+        return false;
+    }
+    ESP_LOG_BUFFER_HEXDUMP(TAG, at.get_buf(), len, ESP_LOG_VERBOSE);
+    at.start_sending(len);
+    return true;
+}
+
+bool DCE::accept_sock()
+{
+    struct timeval tv = {
+        .tv_sec = 0,
+        .tv_usec = 500000,
+    };
+    fd_set fdset;
+    FD_ZERO(&fdset);
+    FD_SET(listen_sock, &fdset);
+    int s = select(listen_sock + 1, &fdset, nullptr, nullptr, &tv);
+    if (s > 0 && FD_ISSET(listen_sock, &fdset)) {
+        struct sockaddr_in source_addr = {};
+        socklen_t addr_len = sizeof(source_addr);
+        sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
+        if (sock < 0) {
+            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
+            return false;
+        }
+        ESP_LOGD(TAG, "Socket accepted!");
+        FD_ZERO(&fdset);
+        return true;
+    } else if (s == 0) {
+        return true;
+    }
+    return false;
+}
+
+void DCE::start_listening(int port)
+{
+    listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+    if (listen_sock < 0) {
+        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
+        return;
+    }
+    int opt = 1;
+    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+    ESP_LOGI(TAG, "Socket created");
+    struct sockaddr_in addr = { };
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+//    inet_aton("127.0.0.1", &addr.sin_addr);
+
+    int err = bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
+    if (err != 0) {
+        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
+        return;
+    }
+    ESP_LOGI(TAG, "Socket bound, port %d", 1883);
+    err = listen(listen_sock, 1);
+    if (err != 0) {
+        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
+        return;
+    }
+
+}
+
+bool DCE::connect(std::string host, int port)
+{
+    dte->on_read(nullptr);
+    tcp_close();
+    dte->on_read([this](uint8_t *data, size_t len) {
+        this->perform_at(data, len);
+        return esp_modem::command_result::TIMEOUT;
+    });
+    if (!at.start_connecting(host, port)) {
+        ESP_LOGE(TAG, "Unable to start connecting");
+        dte->on_read(nullptr);
+        return false;
+    }
+    state = status::CONNECTING;
+    return true;
+}
+
+bool DCE::init()
+{
+    esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
+    esp_vfs_eventfd_register(&config);
+
+    data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
+    assert(data_ready_fd > 0);
+
+    dte->on_read(nullptr);
+    const int retries = 5;
+    int i = 0;
+    while (sync() != esp_modem::command_result::OK) {
+        if (i++ > retries) {
+            ESP_LOGE(TAG, "Failed to sync up");
+            return false;
+        }
+        esp_modem::Task::Delay(1000);
+    }
+    ESP_LOGD(TAG, "Modem in sync");
+    i = 0;
+    while (setup_data_mode() != true) {
+        if (i++ > retries) {
+            ESP_LOGE(TAG, "Failed to setup pdp/data");
+            return false;
+        }
+        esp_modem::Task::Delay(1000);
+    }
+    ESP_LOGD(TAG, "PDP configured");
+    i = 0;
+    while (net_open() != esp_modem::command_result::OK) {
+        if (i++ > retries) {
+            ESP_LOGE(TAG, "Failed to open network");
+            return false;
+        }
+        net_close();
+        esp_modem::Task::Delay(1000);
+    }
+    ESP_LOGD(TAG, "Network opened");
+    i = 0;
+    std::string ip_addr;
+    while (get_ip(ip_addr) != esp_modem::command_result::OK) {
+        if (i++ > retries) {
+            ESP_LOGE(TAG, "Failed obtain an IP address");
+            return false;
+        }
+        esp_modem::Task::Delay(5000);
+    }
+    ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
+    return true;
+}
+
+
+class Factory: public ::esp_modem::dce_factory::Factory {
+public:
+    static std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
+    {
+        return esp_modem::dce_factory::Factory::build_module_T<DCE, std::unique_ptr<DCE>>(config, std::move(dte));
+    }
+};
+
+std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
+{
+    return Factory::create(config, std::move(dte));
+}
+
+
+/**
+ * @brief Opens network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::net_open()
+{
+    return sock_commands::net_open(dte.get() );
+}
+/**
+ * @brief Closes network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::net_close()
+{
+    return sock_commands::net_close(dte.get() );
+}
+/**
+ * @brief Opens a TCP connection
+ * @param[in] host Host name or IP address to connect to
+ * @param[in] port Port number
+ * @param[in] timeout Connection timeout
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::tcp_open(const std::string &host, int port, int timeout )
+{
+    return sock_commands::tcp_open(dte.get(), host, port, timeout );
+}
+/**
+ * @brief Closes opened TCP socket
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::tcp_close()
+{
+    return sock_commands::tcp_close(dte.get() );
+}
+/**
+ * @brief Gets modem IP address
+ * @param[out] addr String representation of modem's IP
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::get_ip(std::string &addr )
+{
+    return sock_commands::get_ip(dte.get(), addr );
+}
+/**
+ * @brief Sets Rx mode
+ * @param[in] mode 0=auto, 1=manual
+ * @return OK, FAIL or TIMEOUT
+ */
+esp_modem::command_result DCE::set_rx_mode(int mode )
+{
+    return sock_commands::set_rx_mode(dte.get(), mode );
+}
+} // namespace sock_dce
diff --git a/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.hpp b/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.hpp
new file mode 100644
index 0000000000..4469e50cd7
--- /dev/null
+++ b/components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.hpp
@@ -0,0 +1,241 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_modem_config.h"
+#include "cxx_include/esp_modem_api.hpp"
+#include <cxx_include/esp_modem_dce_factory.hpp>
+#include "sock_commands.hpp"
+#include <sys/socket.h>
+
+#pragma  once
+
+namespace sock_dce {
+
+
+enum class status {
+    IDLE,
+    CONNECTING,
+    SENDING,
+    RECEIVING,
+    FAILED,
+    PENDING
+};
+
+class Responder {
+public:
+    enum class ret {
+        OK, FAIL, IN_PROGRESS, NEED_MORE_DATA, NEED_MORE_TIME
+    };
+    Responder(int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg):
+        sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
+    ret process_data(status state, uint8_t *data, size_t len);
+    ret check_async_replies(status state, std::string_view &response);
+
+    void start_sending(size_t len);
+    void start_receiving(size_t len);
+    bool start_connecting(std::string host, int port);
+    status pending();
+    uint8_t *get_buf()
+    {
+        return &buffer[0];
+    }
+    size_t get_buf_len()
+    {
+        return buffer_size;
+    }
+
+    void clear_offsets()
+    {
+        actual_read = 0;
+    }
+
+    size_t data_available()
+    {
+        return actual_read;
+    }
+
+    size_t has_data()
+    {
+        return total_len;
+    }
+
+private:
+    static constexpr size_t buffer_size = 512;
+
+    bool on_read(char *data, size_t len)
+    {
+#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
+        ::send(sock, data, len, 0);
+#else
+        ::memcpy(&buffer[actual_read], data, len);
+        actual_read += len;
+#endif
+        return true;
+    }
+
+    ret recv(uint8_t *data, size_t len);
+    ret send(uint8_t *data, size_t len);
+    ret send(std::string_view response);
+    ret connect(std::string_view response);
+    void send_cmd(std::string_view command)
+    {
+        dte->write((uint8_t *) command.begin(), command.size());
+    }
+    std::array<uint8_t, buffer_size> buffer;
+    size_t data_to_recv = 0;
+    size_t actual_read = 0;
+    size_t total_len = 0;
+
+    bool read_again = false;
+    int &sock;
+    int &data_ready_fd;
+    int send_stat = 0;
+    size_t data_to_send = 0;
+    std::shared_ptr<esp_modem::DTE> &dte;
+};
+
+class DCE : public ::esp_modem::GenericModule {
+    using esp_modem::GenericModule::GenericModule;
+public:
+
+    /**
+     * @brief Opens network in AT command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result net_open();
+    /**
+     * @brief Closes network in AT command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result net_close();
+    /**
+     * @brief Opens a TCP connection
+     * @param[in] host Host name or IP address to connect to
+     * @param[in] port Port number
+     * @param[in] timeout Connection timeout
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result tcp_open(const std::string &host, int port, int timeout );
+    /**
+     * @brief Closes opened TCP socket
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result tcp_close();
+    /**
+     * @brief Gets modem IP address
+     * @param[out] addr String representation of modem's IP
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_ip(std::string &addr );
+    /**
+     * @brief Sets Rx mode
+     * @param[in] mode 0=auto, 1=manual
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_rx_mode(int mode );
+    bool init();
+    bool connect(std::string host, int port);
+    void start_listening(int port);
+    bool perform_sock();
+    void set_idle()
+    {
+        signal.set(IDLE);
+    }
+    bool wait_to_idle(uint32_t ms)
+    {
+        if (!signal.wait(IDLE, ms)) {
+            ESP_LOGE("dce", "Failed to get idle");
+            return false;
+        }
+        if (state != status::IDLE) {
+            ESP_LOGE("dce", "Unexpected state %d", static_cast<int>(state));
+            return false;
+        }
+        return true;
+    }
+    int sync_recv(char *buffer, int len, int timeout_ms)
+    {
+        if (!wait_to_idle(timeout_ms)) {
+            return 0;
+        }
+        at.clear_offsets();
+        state = status::RECEIVING;
+        uint64_t data;
+        read(data_ready_fd, &data, sizeof(data));
+        int max_len = std::min(len, (int)at.get_buf_len());
+        at.start_receiving(max_len);
+        if (!signal.wait(IDLE, 500 + timeout_ms)) {
+            return 0;
+        }
+        int ret = at.data_available();
+        if (ret > 0) {
+            memcpy(buffer, at.get_buf(), ret);
+        }
+        set_idle();
+        return ret;
+    }
+    int sync_send(const char *buffer, size_t len, int timeout_ms)
+    {
+        int len_to_send = std::min(len, at.get_buf_len());
+        if (!wait_to_idle(timeout_ms)) {
+            return -1;
+        }
+        state = status::SENDING;
+        memcpy(at.get_buf(), buffer, len_to_send);
+        ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
+        at.start_sending(len_to_send);
+        if (!signal.wait(IDLE, timeout_ms + 1000)) {
+            if (state == status::PENDING) {
+                state = status::IDLE;
+            } else {
+                return -1;
+            }
+        }
+        set_idle();
+        return len_to_send;
+    }
+    int wait_to_read(uint32_t ms)
+    {
+        if (at.has_data() > 0) {
+            ESP_LOGD("dce", "Data buffered in modem (len=%d)", at.has_data());
+            return 1;
+        }
+        struct timeval tv = {
+            .tv_sec = static_cast<time_t>(ms / 1000),
+            .tv_usec = 0,
+        };
+        fd_set fdset;
+        FD_ZERO(&fdset);
+        FD_SET(data_ready_fd, &fdset);
+        int s = select(data_ready_fd + 1, &fdset, nullptr, nullptr, &tv);
+        if (s == 0) {
+            return 0;
+        } else if (s < 0) {
+            ESP_LOGE("dce", "select error %d", errno);
+            return -1;
+        }
+        if (FD_ISSET(data_ready_fd, &fdset)) {
+            ESP_LOGD("dce", "select read: modem data available");
+            return 1;
+        }
+        return -1;
+    }
+private:
+    esp_modem::SignalGroup signal;
+    void close_sock();
+    bool accept_sock();
+    bool sock_to_at();
+    bool at_to_sock();
+    void perform_at(uint8_t *data, size_t len);
+    status state{status::IDLE};
+    static constexpr uint8_t IDLE = 1;
+    Responder at{sock, data_ready_fd, dte};
+    int sock {-1};
+    int listen_sock {-1};
+    int data_ready_fd {-1};
+};
+std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte);
+}
diff --git a/components/esp_modem/examples/modem_tcp_client/main/generate/sock_commands.hpp b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_commands.hpp
new file mode 100644
index 0000000000..7de1bc90f5
--- /dev/null
+++ b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_commands.hpp
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "cxx_include/esp_modem_dte.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+
+
+namespace sock_commands {
+
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+        esp_modem::return_type name(esp_modem::CommandableIf *t ESP_MODEM_COMMAND_PARAMS_AFTER(__VA_ARGS__));
+
+#include "socket_commands.inc"
+#undef ESP_MODEM_DECLARE_DCE_COMMAND
+
+}
diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.cpp
similarity index 93%
rename from components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp
rename to components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.cpp
index db18db9a5d..d039d46a34 100644
--- a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp
+++ b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.cpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -304,23 +304,13 @@ std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr
     return Factory::create(config, std::move(dte));
 }
 
-// Helper macros to handle multiple arguments of declared API
-#define ARGS0
-#define ARGS1 , p1
-#define ARGS2 , p1 , p2
-#define ARGS3 , p1 , p2 , p3
 
-#define EXPAND_ARGS(x)  ARGS ## x
-#define ARGS(x)  EXPAND_ARGS(x)
-
-//
-// Repeat all declarations and forward to the AT commands defined in ::sock_commands namespace
-//
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
-     esp_modem::return_type DCE::name(__VA_ARGS__) { return sock_commands::name(dte.get() ARGS(arg_nr) ); }
-
-DECLARE_SOCK_COMMANDS(return_type name(...) )
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+     esp_modem::return_type DCE::name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__)) { return sock_commands::name(dte.get() ESP_MODEM_COMMAND_FORWARD_AFTER(__VA_ARGS__) ); }
 
+#include "socket_commands.inc"
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
 } // namespace sock_dce
diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.hpp
similarity index 94%
rename from components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp
rename to components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.hpp
index 6e2273f779..d8f27487dc 100644
--- a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp
+++ b/components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.hpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -7,7 +7,6 @@
 #include "esp_modem_config.h"
 #include "cxx_include/esp_modem_api.hpp"
 #include <cxx_include/esp_modem_dce_factory.hpp>
-#include "socket_commands.inc"
 #include "sock_commands.hpp"
 #include <sys/socket.h>
 
@@ -102,11 +101,12 @@ class DCE : public ::esp_modem::GenericModule {
     using esp_modem::GenericModule::GenericModule;
 public:
 
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-esp_modem::return_type name(__VA_ARGS__);
-
-    DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
 
+#include "socket_commands.inc"
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
     bool init();
diff --git a/components/esp_modem/examples/modem_tcp_client/main/generate/socket_commands.inc b/components/esp_modem/examples/modem_tcp_client/main/generate/socket_commands.inc
new file mode 100644
index 0000000000..0e79431015
--- /dev/null
+++ b/components/esp_modem/examples/modem_tcp_client/main/generate/socket_commands.inc
@@ -0,0 +1,40 @@
+/**
+ * @brief Opens network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result)
+
+/**
+ * @brief Closes network in AT command mode
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result)
+
+/**
+ * @brief Opens a TCP connection
+ * @param[in] host Host name or IP address to connect to
+ * @param[in] port Port number
+ * @param[in] timeout Connection timeout
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, STR_IN(host), INT_IN(port), INT_IN(timeout))
+
+/**
+ * @brief Closes opened TCP socket
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result)
+
+/**
+ * @brief Gets modem IP address
+ * @param[out] addr String representation of modem's IP
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, STR_OUT(addr))
+
+/**
+ * @brief Sets Rx mode
+ * @param[in] mode 0=auto, 1=manual
+ * @return OK, FAIL or TIMEOUT
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, INT_IN(mode))
diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_commands.hpp b/components/esp_modem/examples/modem_tcp_client/main/sock_commands.hpp
deleted file mode 100644
index 13ebf83c65..0000000000
--- a/components/esp_modem/examples/modem_tcp_client/main/sock_commands.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#pragma once
-
-#include "cxx_include/esp_modem_dte.hpp"
-#include "cxx_include/esp_modem_dce_module.hpp"
-#include "cxx_include/esp_modem_types.hpp"
-#include "socket_commands.inc"
-
-namespace sock_commands {
-
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-        esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);
-
-DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
-
-#undef ESP_MODEM_DECLARE_DCE_COMMAND
-
-}
diff --git a/components/esp_modem/examples/modem_tcp_client/main/socket_commands.inc b/components/esp_modem/examples/modem_tcp_client/main/socket_commands.inc
deleted file mode 100644
index 1036df84c1..0000000000
--- a/components/esp_modem/examples/modem_tcp_client/main/socket_commands.inc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2021-2022 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.
-#pragma once
-
-#include "generate/esp_modem_command_declare_helper.inc"
-
-#define DECLARE_SOCK_COMMANDS(...) \
-/**
- * @brief Opens network in AT command mode
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result, 0) \
-    \
-/**
- * @brief Closes network in AT command mode
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result, 0) \
-    \
-/**
- * @brief Opens a TCP connection
- * @param[in] host Host name or IP address to connect to
- * @param[in] port Port number
- * @param[in] timeout Connection timeout
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, 3, STRING_IN(p1, host), INT_IN(p2, port), INT_IN(p3, timeout)) \
-    \
-/**
- * @brief Closes opened TCP socket
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result, 0) \
-    \
-/**
- * @brief Gets modem IP address
- * @param[out] addr String representation of modem's IP
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, 1, STRING_OUT(p1, addr)) \
-    \
-/**
- * @brief Sets Rx mode
- * @param[in] mode 0=auto, 1=manual
- * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, 1, INT_IN(p1, mode))
diff --git a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/CMakeLists.txt b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/CMakeLists.txt
index 86e1fa24a3..062b1f65a4 100644
--- a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/CMakeLists.txt
+++ b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/CMakeLists.txt
@@ -1,5 +1,11 @@
+if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
+    set(command_dir "generate")
+else()
+    set(command_dir "command")
+endif()
+
 idf_component_register(SRCS "SIM7070_gnss.cpp"
-                    INCLUDE_DIRS "."
+                    INCLUDE_DIRS "." "${command_dir}"
                     PRIV_REQUIRES esp_modem)
 
 set_target_properties(${COMPONENT_LIB} PROPERTIES
diff --git a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp
new file mode 100644
index 0000000000..10e836110f
--- /dev/null
+++ b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp
@@ -0,0 +1,400 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+//
+// Created on: 23.08.2022
+// Author: franz
+
+
+#pragma once
+
+
+#include "cxx_include/esp_modem_dce_factory.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+
+#include "sim70xx_gps.h"
+
+using PdpContext = esp_modem::PdpContext;
+
+/**
+ * @brief Definition of a custom SIM7070 class with GNSS capabilities.
+ * This inherits from the official esp-modem's SIM7070 device which contains all common library methods.
+ * On top of that, the SIM7070_gnss adds reading GNSS information, which is implemented in a private component.
+ */
+class SIM7070_gnss: public esp_modem::SIM7070 {
+    using SIM7070::SIM7070;
+public:
+    esp_modem::command_result get_gnss_information_sim70xx(sim70xx_gps_t &gps);
+};
+
+/**
+ * @brief DCE for the SIM7070_gnss. Here we've got to forward the general commands, aa well as the GNSS one.
+ */
+class DCE_gnss : public esp_modem::DCE_T<SIM7070_gnss> {
+public:
+
+    using DCE_T<SIM7070_gnss>::DCE_T;
+
+    /**
+     * @brief Sends the initial AT sequence to sync up with the device
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result sync()
+    {
+        return device->sync();
+    }
+    /**
+     * @brief Reads the operator name
+     * @param[out] name operator name
+     * @param[out] act access technology
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_operator_name(std::string &name, int &act )
+    {
+        return device->get_operator_name(name, act );
+    }
+    /**
+     * @brief Stores current user profile
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result store_profile()
+    {
+        return device->store_profile();
+    }
+    /**
+     * @brief Sets the supplied PIN code
+     * @param[in] pin Pin
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_pin(const std::string &pin )
+    {
+        return device->set_pin(pin );
+    }
+    /**
+     * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
+     * @param[in] cmd String command that's send to DTE
+     * @param[out] out Raw output from DTE
+     * @param[in] pass Pattern in response for the API to return OK
+     * @param[in] fail Pattern in response for the API to return FAIL
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result at_raw(const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout )
+    {
+        return device->at_raw(cmd, out, pass, fail, timeout );
+    }
+    /**
+     * @brief Execute the supplied AT command
+     * @param[in] cmd AT command
+     * @param[out] out Command output string
+     * @param[in] timeout AT command timeout in milliseconds
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result at(const std::string &cmd, std::string &out, int timeout )
+    {
+        return device->at(cmd, out, timeout );
+    }
+    /**
+     * @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
+     */
+    esp_modem::command_result read_pin(bool &pin_ok )
+    {
+        return device->read_pin(pin_ok );
+    }
+    /**
+     * @brief Sets echo mode
+     * @param[in] echo_on true if echo mode on (repeats the commands)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_echo(const bool echo_on )
+    {
+        return device->set_echo(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
+     */
+    esp_modem::command_result sms_txt_mode(const bool txt )
+    {
+        return device->sms_txt_mode(txt );
+    }
+    /**
+     * @brief Sets the default (GSM) character set
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result sms_character_set()
+    {
+        return device->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
+     */
+    esp_modem::command_result send_sms(const std::string &number, const std::string &message )
+    {
+        return device->send_sms(number, message );
+    }
+    /**
+     * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result resume_data_mode()
+    {
+        return device->resume_data_mode();
+    }
+    /**
+     * @brief Sets php context
+     * @param[in] p1 PdP context struct to setup modem cellular connection
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_pdp_context(PdpContext &pdp )
+    {
+        return device->set_pdp_context(pdp );
+    }
+    /**
+     * @brief Switches to the command mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_command_mode()
+    {
+        return device->set_command_mode();
+    }
+    /**
+     * @brief Switches to the CMUX mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_cmux()
+    {
+        return device->set_cmux();
+    }
+    /**
+     * @brief Reads the IMSI number
+     * @param[out] imsi Module's IMSI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_imsi(std::string &imsi )
+    {
+        return device->get_imsi(imsi );
+    }
+    /**
+     * @brief Reads the IMEI number
+     * @param[out] imei Module's IMEI number
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_imei(std::string &imei )
+    {
+        return device->get_imei(imei );
+    }
+    /**
+     * @brief Reads the module name
+     * @param[out] name module name
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_module_name(std::string &name )
+    {
+        return device->get_module_name(name );
+    }
+    /**
+     * @brief Sets the modem to data mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_data_mode()
+    {
+        return device->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
+     */
+    esp_modem::command_result get_signal_quality(int &rssi, int &ber )
+    {
+        return device->get_signal_quality(rssi, 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
+     */
+    esp_modem::command_result set_flow_control(int dce_flow, int dte_flow )
+    {
+        return device->set_flow_control(dce_flow, dte_flow );
+    }
+    /**
+     * @brief Hangs up current data call
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result hang_up()
+    {
+        return device->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
+     */
+    esp_modem::command_result get_battery_status(int &voltage, int &bcs, int &bcl )
+    {
+        return device->get_battery_status(voltage, bcs, bcl );
+    }
+    /**
+     * @brief Power down the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result power_down()
+    {
+        return device->power_down();
+    }
+    /**
+     * @brief Reset the module
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result reset()
+    {
+        return device->reset();
+    }
+    /**
+     * @brief Configures the baudrate
+     * @param[in] baud Desired baud rate of the DTE
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_baud(int baud )
+    {
+        return device->set_baud(baud );
+    }
+    /**
+     * @brief Force an attempt to connect to a specific operator
+     * @param[in] mode mode of attempt
+     * mode=0 - automatic
+     * mode=1 - manual
+     * mode=2 - deregister
+     * mode=3 - set format for read operation
+     * mode=4 - manual with fallback to automatic
+     * @param[in] format what format the operator is given in
+     * format=0 - long format
+     * format=1 - short format
+     * format=2 - numeric
+     * @param[in] oper the operator to connect to
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_operator(int mode, int format, const std::string &oper )
+    {
+        return device->set_operator(mode, format, oper );
+    }
+    /**
+     * @brief Attach or detach from the GPRS service
+     * @param[in] state 1-attach 0-detach
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_attachment_state(int state )
+    {
+        return device->set_network_attachment_state(state );
+    }
+    /**
+     * @brief Get network attachment state
+     * @param[out] state 1-attached 0-detached
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_network_attachment_state(int &state )
+    {
+        return device->get_network_attachment_state(state );
+    }
+    /**
+     * @brief What mode the radio should be set to
+     * @param[in] state state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_radio_state(int state )
+    {
+        return device->set_radio_state(state );
+    }
+    /**
+     * @brief Get current radio state
+     * @param[out] state 1-full 0-minimum ...
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_radio_state(int &state )
+    {
+        return device->get_radio_state(state );
+    }
+    /**
+     * @brief Set network mode
+     * @param[in] mode preferred mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_mode(int mode )
+    {
+        return device->set_network_mode(mode );
+    }
+    /**
+     * @brief Preferred network mode (CAT-M and/or NB-IoT)
+     * @param[in] mode preferred selection
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_preferred_mode(int mode )
+    {
+        return device->set_preferred_mode(mode );
+    }
+    /**
+     * @brief Set network bands for CAT-M or NB-IoT
+     * @param[in] mode CAT-M or NB-IoT
+     * @param[in] bands bitmap in hex representing bands
+     * @param[in] size size of teh bands bitmap
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_network_bands(const std::string &mode, const int *bands, int size )
+    {
+        return device->set_network_bands(mode, bands, size );
+    }
+    /**
+     * @brief Show network system mode
+     * @param[out] mode current network mode
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_network_system_mode(int &mode )
+    {
+        return device->get_network_system_mode(mode );
+    }
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result set_gnss_power_mode(int mode )
+    {
+        return device->set_gnss_power_mode(mode );
+    }
+    /**
+     * @brief GNSS power control
+     * @param[out] mode power mode (0 - off, 1 - on)
+     * @return OK, FAIL or TIMEOUT
+     */
+    esp_modem::command_result get_gnss_power_mode(int &mode )
+    {
+        return device->get_gnss_power_mode(mode );
+    }
+    esp_modem::command_result get_gnss_information_sim70xx(sim70xx_gps_t &gps);
+    esp_modem::command_result get_operator_name(std::string &name)
+    {
+        return device->get_operator_name(name);
+    }
+};
+/**
+ * @brief Helper create method which employs the customized DCE factory for building DCE_gnss objects
+ * @return unique pointer of the specific DCE
+ */
+std::unique_ptr<DCE_gnss> create_SIM7070_GNSS_dce(const esp_modem::dce_config *config,
+        std::shared_ptr<esp_modem::DTE> dte,
+        esp_netif_t *netif);
diff --git a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/SIM7070_gnss.hpp b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp
similarity index 69%
rename from components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/SIM7070_gnss.hpp
rename to components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp
index 345269ba88..71ed0e6e99 100644
--- a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/SIM7070_gnss.hpp
+++ b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -13,8 +13,12 @@
 
 #include "cxx_include/esp_modem_dce_factory.hpp"
 #include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
+
 #include "sim70xx_gps.h"
 
+using PdpContext = esp_modem::PdpContext;
+
 /**
  * @brief Definition of a custom SIM7070 class with GNSS capabilities.
  * This inherits from the official esp-modem's SIM7070 device which contains all common library methods.
@@ -33,22 +37,22 @@ class DCE_gnss : public esp_modem::DCE_T<SIM7070_gnss> {
 public:
 
     using DCE_T<SIM7070_gnss>::DCE_T;
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-    template <typename ...Agrs> \
-    esp_modem::return_type name(Agrs&&... args)   \
-    {   \
-        return device->name(std::forward<Agrs>(args)...); \
-    }
 
-    DECLARE_ALL_COMMAND_APIS(forwards name(...)
-    {
-        device->name(...);
-    } )
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+     esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__)) { return device->name(ESP_MODEM_COMMAND_FORWARD(__VA_ARGS__)); }
 
+#include "esp_modem_command_declare.inc"
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
+
     esp_modem::command_result get_gnss_information_sim70xx(sim70xx_gps_t &gps);
 
+    esp_modem::command_result get_operator_name(std::string &name)
+    {
+        return device->get_operator_name(name);
+    }
 };
 
 
diff --git a/components/esp_modem/examples/simple_cmux_client/main/simple_cmux_client_main.cpp b/components/esp_modem/examples/simple_cmux_client/main/simple_cmux_client_main.cpp
index f1b8e8439c..d87b645134 100644
--- a/components/esp_modem/examples/simple_cmux_client/main/simple_cmux_client_main.cpp
+++ b/components/esp_modem/examples/simple_cmux_client/main/simple_cmux_client_main.cpp
@@ -254,7 +254,7 @@ extern "C" void app_main(void)
 
 
 #if CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070_GNSS == 1
-    esp_modem_gps_t gps;
+    sim70xx_gps_t gps;
 
     for (int i = 0; i < 200; ++i) {
         if (dce->get_gnss_information_sim70xx(gps) == esp_modem::command_result::OK) {
@@ -279,7 +279,7 @@ extern "C" void app_main(void)
             ESP_LOGI(TAG, "gps.dop_h %f gps.dop_p %f gps.dop_v %f ",
                      gps.dop_h,   gps.dop_p,   gps.dop_v );
             ESP_LOGI(TAG, "gps.sats_in_view  %i",
-                     gps.sats_in_view);
+                     gps.sat.num);
             ESP_LOGI(TAG, "gps.hpa  %f gps.vpa  %f",
                      gps.hpa, gps.vpa);
         }
@@ -291,10 +291,12 @@ extern "C" void app_main(void)
 
 #if CONFIG_EXAMPLE_PERFORM_OTA == 1
     esp_http_client_config_t config = { };
+    esp_https_ota_config_t ota_config = { };
+    ota_config.http_config = &config;
     config.skip_cert_common_name_check = true;
     config.url = CONFIG_EXAMPLE_PERFORM_OTA_URI;
 
-    esp_err_t ret = esp_https_ota(&config);
+    esp_err_t ret = esp_https_ota(&ota_config);
     if (ret == ESP_OK) {
         esp_restart();
     } else {
diff --git a/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp b/components/esp_modem/generate/include/cxx_include/esp_modem_command_library.hpp
similarity index 78%
rename from components/esp_modem/include/cxx_include/esp_modem_command_library.hpp
rename to components/esp_modem/generate/include/cxx_include/esp_modem_command_library.hpp
index d0e133eebe..7cd0309ad5 100644
--- a/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp
+++ b/components/esp_modem/generate/include/cxx_include/esp_modem_command_library.hpp
@@ -1,16 +1,16 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
 
 #pragma once
 
-#include "esp_modem_dte.hpp"
-#include "esp_modem_dce_module.hpp"
-#include "esp_modem_types.hpp"
-#include "generate/esp_modem_command_declare.inc"
+#include "cxx_include/esp_modem_dte.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+#include "cxx_include/esp_modem_types.hpp"
 
+//  --- ESP-MODEM command module starts here ---
 namespace esp_modem {
 namespace dce_commands {
 
@@ -38,10 +38,11 @@ command_result generic_command(CommandableIf *t, const std::string &command,
 /**
  * @brief Declaration of all commands is generated from esp_modem_command_declare.inc
  */
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-        return_type name(CommandableIf *t, ## __VA_ARGS__);
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+        return_type name(CommandableIf *t  ESP_MODEM_COMMAND_PARAMS_AFTER(__VA_ARGS__));
 
-DECLARE_ALL_COMMAND_APIS(declare name(Commandable *p, ...);)
+#include "esp_modem_command_declare.inc"
 
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
diff --git a/components/esp_modem/generate/include/cxx_include/esp_modem_dce_generic.hpp b/components/esp_modem/generate/include/cxx_include/esp_modem_dce_generic.hpp
new file mode 100644
index 0000000000..0d5d0b5f2c
--- /dev/null
+++ b/components/esp_modem/generate/include/cxx_include/esp_modem_dce_generic.hpp
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+//  --- ESP-MODEM command module starts here ---
+namespace esp_modem {
+
+/**
+ * @defgroup ESP_MODEM_DCE
+ * @brief Definition of DCE abstraction
+ */
+
+/** @addtogroup ESP_MODEM_DCE
+ * @{
+ */
+
+/**
+ * @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported
+ * devices and most common modems, as well.
+ */
+class DCE : public DCE_T<GenericModule> {
+public:
+
+    command_result get_operator_name(std::string &name)
+    {
+        return device->get_operator_name(name);
+    }
+
+    using DCE_T<GenericModule>::DCE_T;
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+    return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__))   \
+    { \
+        return device->name(ESP_MODEM_COMMAND_FORWARD(__VA_ARGS__)); \
+    }
+#include "esp_modem_command_declare.inc"
+
+#undef ESP_MODEM_DECLARE_DCE_COMMAND
+
+};
+
+/**
+ * @}
+ */
+
+} // esp_modem
diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp b/components/esp_modem/generate/include/cxx_include/esp_modem_dce_module.hpp
similarity index 91%
rename from components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp
rename to components/esp_modem/generate/include/cxx_include/esp_modem_dce_module.hpp
index 6ae36e63ec..ee61faee9e 100644
--- a/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp
+++ b/components/esp_modem/generate/include/cxx_include/esp_modem_dce_module.hpp
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -8,11 +8,13 @@
 
 #include <memory>
 #include <utility>
-#include "generate/esp_modem_command_declare.inc"
+//#include "generate/esp_modem_command_declare.inc"
 #include "cxx_include/esp_modem_command_library.hpp"
 #include "cxx_include/esp_modem_types.hpp"
 #include "esp_modem_dce_config.h"
 
+
+//  --- ESP-MODEM command module starts here ---
 namespace esp_modem {
 
 /**
@@ -110,10 +112,15 @@ class GenericModule: public ModuleIf {
     /**
      * @brief Common DCE commands generated from the API AT list
      */
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-    virtual return_type name(__VA_ARGS__);
+#include "esp_modem_command_declare_helper.inc"
+
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+    virtual return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
+
+//    DECLARE_ALL_COMMAND_APIS(virtual return_type name(...); )
+
+#include "esp_modem_command_declare.inc"
 
-    DECLARE_ALL_COMMAND_APIS(virtual return_type name(...); )
 
 #undef ESP_MODEM_DECLARE_DCE_COMMAND
 
diff --git a/components/esp_modem/generate/include/esp_modem_api.h b/components/esp_modem/generate/include/esp_modem_api.h
new file mode 100644
index 0000000000..6065c2c49b
--- /dev/null
+++ b/components/esp_modem/generate/include/esp_modem_api.h
@@ -0,0 +1,30 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_err.h"
+//#include "generate/esp_modem_command_declare.inc"
+#include "esp_modem_c_api_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+        esp_err_t esp_modem_ ## name(esp_modem_dce_t *dce ESP_MODEM_COMMAND_PARAMS_AFTER(__VA_ARGS__));
+
+#include "esp_modem_command_declare.inc"
+
+#undef ESP_MODEM_DECLARE_DCE_COMMAND
+//  --- ESP-MODEM command module ends here ---
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/components/esp_modem/include/generate/esp_modem_command_declare.inc b/components/esp_modem/generate/include/esp_modem_command_declare.inc
similarity index 65%
rename from components/esp_modem/include/generate/esp_modem_command_declare.inc
rename to components/esp_modem/generate/include/esp_modem_command_declare.inc
index 0f7b122f35..8dfa3c0ae9 100644
--- a/components/esp_modem/include/generate/esp_modem_command_declare.inc
+++ b/components/esp_modem/generate/include/esp_modem_command_declare.inc
@@ -1,49 +1,30 @@
-// Copyright 2021-2022 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.
-#pragma once
-
-#include "generate/esp_modem_command_declare_helper.inc"
-
-
-#define DECLARE_ALL_COMMAND_APIS(...) \
 /**
  * @brief Sends the initial AT sequence to sync up with the device
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result)
+
 /**
  * @brief Reads the operator name
  * @param[out] name operator name
  * @param[out] act access technology
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, 2, STRING_OUT(p1, name), INT_OUT(p2, act)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, STR_OUT(name), INT_OUT(act))
+
 /**
  * @brief Stores current user profile
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result)
+
 /**
  * @brief Sets the supplied PIN code
  * @param[in] pin Pin
  * @return OK, FAIL or TIMEOUT
- */\
-ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, STR_IN(pin))
+
 /**
  * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
  * @param[in] cmd String command that's send to DTE
@@ -52,155 +33,155 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
  * @param[in] fail Pattern in response for the API to return FAIL
  * @param[in] timeout AT command timeout in milliseconds
  * @return OK, FAIL or TIMEOUT
- */\
-ESP_MODEM_DECLARE_DCE_COMMAND(at_raw, command_result, 5, STRING_IN(p1, cmd), STRING_OUT(p2, out), STRING_IN(p3, pass), STRING_IN(p4, fail), INT_IN(p5, timeout)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(at_raw, command_result, STR_IN(cmd), STR_OUT(out), STR_IN(pass), STR_IN(fail), INT_IN(timeout))
+
 /**
  * @brief Execute the supplied AT command
  * @param[in] cmd AT command
  * @param[out] out Command output string
  * @param[in] timeout AT command timeout in milliseconds
  * @return OK, FAIL or TIMEOUT
- */\
-ESP_MODEM_DECLARE_DCE_COMMAND(at, command_result, 3, STRING_IN(p1, cmd), STRING_OUT(p2, out), INT_IN(p3, timeout)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(at, command_result, STR_IN(cmd), STR_OUT(out), INT_IN(timeout))
+
 /**
  * @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
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, 1, BOOL_OUT(p1, pin_ok)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, BOOL_OUT(pin_ok))
+
 /**
  * @brief Sets echo mode
  * @param[in] echo_on true if echo mode on (repeats the commands)
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, 1, BOOL_IN(p1, echo_on)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, BOOL_IN(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
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(sms_txt_mode, command_result, 1, BOOL_IN(p1, txt)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(sms_txt_mode, command_result, BOOL_IN(txt))
+
 /**
  * @brief Sets the default (GSM) character set
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(sms_character_set, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(sms_character_set, command_result)
+
 /**
  * @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
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(send_sms, command_result, 2, STRING_IN(p1, number), STRING_IN(p2, message)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(send_sms, command_result, STR_IN(number), STR_IN(message))
+
 /**
  * @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result)
+
 /**
  * @brief Sets php context
  * @param[in] p1 PdP context struct to setup modem cellular connection
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, 1, STRUCT_OUT(PdpContext, p1)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, STRUCT_OUT(PdpContext, pdp))
+
 /**
  * @brief Switches to the command mode
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_command_mode, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_command_mode, command_result)
+
 /**
  * @brief Switches to the CMUX mode
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result)
+
 /**
  * @brief Reads the IMSI number
  * @param[out] imsi Module's IMSI number
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, 1, STRING_OUT(p1, imsi)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, STR_OUT(imsi))
+
 /**
  * @brief Reads the IMEI number
  * @param[out] imei Module's IMEI number
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, 1, STRING_OUT(p1, imei)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, STR_OUT(imei))
+
 /**
  * @brief Reads the module name
  * @param[out] name module name
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, 1, STRING_OUT(p1, name)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, STR_OUT(name))
+
 /**
  * @brief Sets the modem to data mode
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_data_mode, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_data_mode, command_result)
+
 /**
  * @brief Get Signal quality
  * @param[out] rssi signal strength indication
  * @param[out] ber channel bit error rate
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, INT_OUT(p1, rssi), INT_OUT(p2, ber)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, INT_OUT(rssi), INT_OUT(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
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_flow_control, command_result, 2, INT_IN(p1, dce_flow), INT_IN(p2, dte_flow)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_flow_control, command_result, INT_IN(dce_flow), INT_IN(dte_flow))
+
 /**
  * @brief Hangs up current data call
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(hang_up, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(hang_up, command_result)
+
 /**
  * @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
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_battery_status, command_result, 3, INT_OUT(p1, voltage), INT_OUT(p2, bcs), INT_OUT(p3, bcl)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_battery_status, command_result, INT_OUT(voltage), INT_OUT(bcs), INT_OUT(bcl))
+
 /**
  * @brief Power down the module
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(power_down, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(power_down, command_result)
+
 /**
  * @brief Reset the module
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(reset, command_result, 0) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(reset, command_result)
+
 /**
  * @brief Configures the baudrate
  * @param[in] baud Desired baud rate of the DTE
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(p1, baud)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, INT_IN(baud))
+
 /**
  * @brief Force an attempt to connect to a specific operator
  * @param[in] mode mode of attempt
@@ -215,82 +196,80 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(p1, baud)) \
  * format=2 - numeric
  * @param[in] oper the operator to connect to
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_operator, command_result, 3, INT_IN(p1, mode), INT_IN(p2, format), STRING_IN(p3, oper)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_operator, command_result, INT_IN(mode), INT_IN(format), STR_IN(oper))
+
 /**
  * @brief Attach or detach from the GPRS service
  * @param[in] state 1-attach 0-detach
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_network_attachment_state, command_result, 1, INT_IN(p1, state)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_network_attachment_state, command_result, INT_IN(state))
+
 /**
  * @brief Get network attachment state
  * @param[out] state 1-attached 0-detached
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_network_attachment_state, command_result, 1, INT_OUT(p1, state)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_network_attachment_state, command_result, INT_OUT(state))
+
 /**
  * @brief What mode the radio should be set to
  * @param[in] state state 1-full 0-minimum ...
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_radio_state, command_result, 1, INT_IN(p1, state)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_radio_state, command_result, INT_IN(state))
+
 /**
  * @brief Get current radio state
  * @param[out] state 1-full 0-minimum ...
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_radio_state, command_result, 1, INT_OUT(p1, state)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_radio_state, command_result, INT_OUT(state))
+
 /**
  * @brief Set network mode
  * @param[in] mode preferred mode
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_network_mode, command_result, 1, INT_IN(p1, mode)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_network_mode, command_result, INT_IN(mode))
+
 /**
  * @brief Preferred network mode (CAT-M and/or NB-IoT)
  * @param[in] mode preferred selection
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_preferred_mode, command_result, 1, INT_IN(p1, mode)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_preferred_mode, command_result, INT_IN(mode))
+
 /**
  * @brief Set network bands for CAT-M or NB-IoT
  * @param[in] mode CAT-M or NB-IoT
  * @param[in] bands bitmap in hex representing bands
  * @param[in] size size of teh bands bitmap
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_network_bands, command_result, 3, STRING_IN(p1, mode), INTEGER_LIST_IN(p2, bands), INT_IN(p3, size)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_network_bands, command_result, STR_IN(mode), INT_LIST_IN(bands), INT_IN(size))
+
 /**
  * @brief Show network system mode
  * @param[out] mode current network mode
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_network_system_mode, command_result, 1, INT_OUT(p1, mode)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_network_system_mode, command_result, INT_OUT(mode))
+
 /**
  * @brief GNSS power control
  * @param[out] mode power mode (0 - off, 1 - on)
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(set_gnss_power_mode, command_result, 1, INT_IN(p1, mode)) \
-    \
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(set_gnss_power_mode, command_result, INT_IN(mode))
+
 /**
  * @brief GNSS power control
  * @param[out] mode power mode (0 - off, 1 - on)
  * @return OK, FAIL or TIMEOUT
- */ \
-ESP_MODEM_DECLARE_DCE_COMMAND(get_gnss_power_mode, command_result, 1, INT_OUT(p1, mode)) \
-    \
-
+ */
+ESP_MODEM_DECLARE_DCE_COMMAND(get_gnss_power_mode, command_result, INT_OUT(mode))
 
 #ifdef GENERATE_DOCS
 // cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC  -xc++ -I../include -DGENERATE_DOCS  - | sed -n '1,/DCE command documentation/!p'
diff --git a/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc b/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc
new file mode 100644
index 0000000000..dbd6c0fe84
--- /dev/null
+++ b/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc
@@ -0,0 +1,58 @@
+#pragma once
+
+#define ESP_MODEM_GET_MACRO(_0, _1, _2, _3, _4, _5, _6, ESP_MODEM_MACRO_NAME, ...) ESP_MODEM_MACRO_NAME
+#define ESP_MODEM_COMMAND_PARAMS(...) ESP_MODEM_GET_MACRO(_0, ##__VA_ARGS__, ESP_MODEM_HELPER6, ESP_MODEM_HELPER5, ESP_MODEM_HELPER4, ESP_MODEM_HELPER3, ESP_MODEM_HELPER2, ESP_MODEM_HELPER1, ESP_MODEM_HELPER0)(PARAM_, ESP_MODEM_NO_COMMA, ESP_MODEM_NO_COMMA, ##__VA_ARGS__)
+#define ESP_MODEM_COMMAND_PARAMS_AFTER(...) ESP_MODEM_GET_MACRO(_0, ##__VA_ARGS__, ESP_MODEM_HELPER6, ESP_MODEM_HELPER5, ESP_MODEM_HELPER4, ESP_MODEM_HELPER3, ESP_MODEM_HELPER2, ESP_MODEM_HELPER1, ESP_MODEM_HELPER0)(PARAM_, ESP_MODEM_COMMA, ESP_MODEM_NO_COMMA, ##__VA_ARGS__)
+#define ESP_MODEM_COMMAND_FORWARD(...) ESP_MODEM_GET_MACRO(_0, ##__VA_ARGS__, ESP_MODEM_HELPER6, ESP_MODEM_HELPER5, ESP_MODEM_HELPER4, ESP_MODEM_HELPER3, ESP_MODEM_HELPER2, ESP_MODEM_HELPER1, ESP_MODEM_HELPER0)(FORWARD_, ESP_MODEM_NO_COMMA, ESP_MODEM_NO_COMMA, ##__VA_ARGS__)
+#define ESP_MODEM_COMMAND_FORWARD_AFTER(...) ESP_MODEM_GET_MACRO(_0, ##__VA_ARGS__, ESP_MODEM_HELPER6, ESP_MODEM_HELPER5, ESP_MODEM_HELPER4, ESP_MODEM_HELPER3, ESP_MODEM_HELPER2, ESP_MODEM_HELPER1, ESP_MODEM_HELPER0)(FORWARD_, ESP_MODEM_COMMA, ESP_MODEM_NO_COMMA, ##__VA_ARGS__)
+
+#ifdef __cplusplus
+#define PARAM_STR_OUT(name) std::string& name
+#define PARAM_STR_IN(name) const std::string& name
+#define PARAM_INT_OUT(name) int& name
+#define PARAM_INT_IN(name) int name
+#define PARAM_BOOL_IN(name) const bool name
+#define PARAM_BOOL_OUT(name) bool& name
+#define PARAM_STRUCT_OUT(struct_name, name)  struct_name& name
+#define PARAM_INT_LIST_IN(name) const int* name
+#else
+#define PARAM_STR_OUT(name) char* name
+#define PARAM_STR_IN(name)  const char* name
+#define PARAM_INT_OUT(name) int* name
+#define PARAM_INT_IN(name) int name
+#define PARAM_BOOL_IN(name) const bool name
+#define PARAM_BOOL_OUT(name) bool* name
+#define PARAM_STRUCT_OUT(struct_name, name)  esp_modem_ ## struct_name ## _t* name
+#define PARAM_INT_LIST_IN(name) const int* name
+#endif
+
+#define FORWARD_STR_OUT(name) name
+#define FORWARD_STR_IN(name) name
+#define FORWARD_INT_OUT(name) name
+#define FORWARD_INT_IN(name) name
+#define FORWARD_BOOL_IN(name) name
+#define FORWARD_BOOL_OUT(name) name
+#define FORWARD_STRUCT_OUT(struct_name, name) name
+#define FORWARD_INT_LIST_IN(name) name
+
+
+#define ESP_MODEM_COMMA ,
+#define ESP_MODEM_NO_COMMA
+
+#define ESP_MODEM_HELPER_EVAL(x) x
+#define ESP_MODEM_HELPER_GENERIC(a, b) ESP_MODEM_HELPER_EVAL(a ## b)
+#define ESP_MODEM_HELPER0(prefix, lead_comma, trail_comma)
+#define ESP_MODEM_HELPER1(prefix, lead_comma, trail_comma, p1) lead_comma ESP_MODEM_HELPER_GENERIC(prefix, p1) trail_comma
+#define ESP_MODEM_HELPER2(prefix, lead_comma, trail_comma, p1, p2) lead_comma ESP_MODEM_HELPER_GENERIC(prefix, p1), ESP_MODEM_HELPER_GENERIC(prefix, p2) trail_comma
+#define ESP_MODEM_HELPER3(prefix, lead_comma, trail_comma, p1, p2, p3) lead_comma ESP_MODEM_HELPER_GENERIC(prefix, p1), ESP_MODEM_HELPER_GENERIC(prefix, p2), ESP_MODEM_HELPER_GENERIC(prefix, p3) trail_comma
+#define ESP_MODEM_HELPER4(prefix, lead_comma, trail_comma, p1, p2, p3, p4) lead_comma \
+        ESP_MODEM_HELPER_GENERIC(prefix, p1), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p2), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p3), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p4) trail_comma
+#define ESP_MODEM_HELPER5(prefix, lead_comma, trail_comma, p1, p2, p3, p4, p5) lead_comma \
+        ESP_MODEM_HELPER_GENERIC(prefix, p1), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p2), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p3), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p4), \
+        ESP_MODEM_HELPER_GENERIC(prefix, p5) trail_comma
diff --git a/components/esp_modem/src/esp_modem_modules.cpp b/components/esp_modem/generate/src/esp_modem_modules.cpp
similarity index 71%
rename from components/esp_modem/src/esp_modem_modules.cpp
rename to components/esp_modem/generate/src/esp_modem_modules.cpp
index 5634034946..76b29e3a78 100644
--- a/components/esp_modem/src/esp_modem_modules.cpp
+++ b/components/esp_modem/generate/src/esp_modem_modules.cpp
@@ -6,36 +6,34 @@
 
 #include "cxx_include/esp_modem_api.hpp"
 #include "cxx_include/esp_modem_dce_module.hpp"
-#include "generate/esp_modem_command_declare.inc"
+#include "cxx_include/esp_modem_dte.hpp"
 
+//  --- ESP-MODEM command module starts here ---
 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 , p1
-#define ARGS2 , p1 , p2
-#define ARGS3 , p1 , p2 , p3
-#define ARGS4 , p1 , p2 , p3, p4
-#define ARGS5 , p1 , p2 , p3, p4, p5
-#define ARGS6 , p1 , p2 , p3, p4, p5, p6
+#include "esp_modem_command_declare_helper.inc"
+
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, type, ...) \
+    type GenericModule::name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__)) { \
+        return esp_modem::dce_commands::name(dte.get() ESP_MODEM_COMMAND_FORWARD_AFTER(__VA_ARGS__)); }
+
+#include "esp_modem_command_declare.inc"
+
+
+
+// Usage examples:
+// Zero arguments
 
-#define _ARGS(x)  ARGS ## x
-#define ARGS(x)  _ARGS(x)
+// Helper to apply the correct macro to each parameter
 
 //
 // 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
 
diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp
index 045783c2dd..3a82e2ddd6 100644
--- a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp
+++ b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp
@@ -1,141 +1,10 @@
 /*
- * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
 
 #pragma once
 
-#include <utility>
-#include "cxx_include/esp_modem_netif.hpp"
-#include "cxx_include/esp_modem_dce_module.hpp"
-
-namespace esp_modem {
-
-/**
- * @defgroup ESP_MODEM_DCE
- * @brief Definition of DCE abstraction
- */
-/** @addtogroup ESP_MODEM_DCE
- * @{
- */
-
-
-/**
- * @brief Helper class responsible for switching modes of the DCE's
- */
-class DCE_Mode {
-public:
-    DCE_Mode(): mode(modem_mode::UNDEF) {}
-    ~DCE_Mode() = default;
-    bool set(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
-    modem_mode get();
-    modem_mode guess(DTE *dte, bool with_cmux = false);
-
-private:
-    bool set_unsafe(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
-    modem_mode guess_unsafe(DTE *dte, bool with_cmux);
-    modem_mode mode;
-
-};
-
-/**
- * @brief General DCE class templated on a specific module. It is responsible for all the necessary transactions
- * related to switching modes and consequent synergy with aggregated objects of DTE, Netif and a specific Module
- */
-template<class SpecificModule>
-class DCE_T {
-    static_assert(std::is_base_of<ModuleIf, SpecificModule>::value, "DCE must be instantiated with Module class only");
-public:
-    explicit DCE_T(const std::shared_ptr<DTE> &dte, std::shared_ptr<SpecificModule> dev, esp_netif_t *netif):
-        dte(dte), device(std::move(dev)), netif(dte, netif)
-    { }
-
-    ~DCE_T() = default;
-
-    /**
-     * @brief Set data mode!
-     */
-    void set_data()
-    {
-        set_mode(modem_mode::DATA_MODE);
-    }
-
-    void exit_data()
-    {
-        set_mode(modem_mode::COMMAND_MODE);
-    }
-
-    void set_cmux()
-    {
-        set_mode(modem_mode::CMUX_MODE);
-    }
-
-    SpecificModule *get_module()
-    {
-        return device.get();
-    }
-
-    command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms)
-    {
-        return dte->command(command, std::move(got_line), time_ms);
-    }
-
-    modem_mode guess_mode(bool with_cmux = false)
-    {
-        return mode.guess(dte.get(), with_cmux);
-    }
-
-    bool set_mode(modem_mode m)
-    {
-        return mode.set(dte.get(), device.get(), netif, m);
-    }
-
-    bool recover()
-    {
-        return dte->recover();
-    }
-
-#ifdef CONFIG_ESP_MODEM_URC_HANDLER
-    void set_urc(got_line_cb on_read_cb)
-    {
-        dte->set_urc_cb(on_read_cb);
-    }
-#endif
-
-protected:
-    std::shared_ptr<DTE> dte;
-    std::shared_ptr<SpecificModule> device;
-    Netif netif;
-    DCE_Mode mode;
-};
-
-/**
- * @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported
- * devices and most common modems, as well.
- */
-class DCE : public DCE_T<GenericModule> {
-public:
-
-    using DCE_T<GenericModule>::DCE_T;
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-    template <typename ...Agrs> \
-    return_type name(Agrs&&... args)   \
-    {   \
-        return device->name(std::forward<Agrs>(args)...); \
-    }
-
-    DECLARE_ALL_COMMAND_APIS(forwards name(...)
-    {
-        device->name(...);
-    } )
-
-#undef ESP_MODEM_DECLARE_DCE_COMMAND
-
-};
-
-/**
- * @}
- */
-
-} // esp_modem
+#include "cxx_include/esp_modem_dce_template.hpp"
+#include "cxx_include/esp_modem_dce_generic.hpp"
diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce_template.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce_template.hpp
new file mode 100644
index 0000000000..7e689907c0
--- /dev/null
+++ b/components/esp_modem/include/cxx_include/esp_modem_dce_template.hpp
@@ -0,0 +1,117 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <utility>
+#include "cxx_include/esp_modem_netif.hpp"
+#include "cxx_include/esp_modem_dce_module.hpp"
+
+namespace esp_modem {
+
+/**
+ * @defgroup ESP_MODEM_DCE
+ * @brief Definition of DCE abstraction
+ */
+/** @addtogroup ESP_MODEM_DCE
+ * @{
+ */
+
+
+/**
+ * @brief Helper class responsible for switching modes of the DCE's
+ */
+class DCE_Mode {
+public:
+    DCE_Mode(): mode(modem_mode::UNDEF) {}
+    ~DCE_Mode() = default;
+    bool set(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
+    modem_mode get();
+    modem_mode guess(DTE *dte, bool with_cmux = false);
+
+private:
+    bool set_unsafe(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
+    modem_mode guess_unsafe(DTE *dte, bool with_cmux);
+    modem_mode mode;
+
+};
+
+/**
+ * @brief General DCE class templated on a specific module. It is responsible for all the necessary transactions
+ * related to switching modes and consequent synergy with aggregated objects of DTE, Netif and a specific Module
+ */
+template<class SpecificModule>
+class DCE_T {
+    static_assert(std::is_base_of<ModuleIf, SpecificModule>::value, "DCE must be instantiated with Module class only");
+public:
+    explicit DCE_T(const std::shared_ptr<DTE> &dte, std::shared_ptr<SpecificModule> dev, esp_netif_t *netif):
+        dte(dte), device(std::move(dev)), netif(dte, netif)
+    { }
+
+    ~DCE_T() = default;
+
+    /**
+     * @brief Set data mode!
+     */
+    void set_data()
+    {
+        set_mode(modem_mode::DATA_MODE);
+    }
+
+    void exit_data()
+    {
+        set_mode(modem_mode::COMMAND_MODE);
+    }
+
+    void set_cmux()
+    {
+        set_mode(modem_mode::CMUX_MODE);
+    }
+
+    SpecificModule *get_module()
+    {
+        return device.get();
+    }
+
+    command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms)
+    {
+        return dte->command(command, std::move(got_line), time_ms);
+    }
+
+    modem_mode guess_mode(bool with_cmux = false)
+    {
+        return mode.guess(dte.get(), with_cmux);
+    }
+
+    bool set_mode(modem_mode m)
+    {
+        return mode.set(dte.get(), device.get(), netif, m);
+    }
+
+    bool recover()
+    {
+        return dte->recover();
+    }
+
+#ifdef CONFIG_ESP_MODEM_URC_HANDLER
+    void set_urc(got_line_cb on_read_cb)
+    {
+        dte->set_urc_cb(on_read_cb);
+    }
+#endif
+
+protected:
+    std::shared_ptr<DTE> dte;
+    std::shared_ptr<SpecificModule> device;
+    Netif netif;
+    DCE_Mode mode;
+};
+
+/**
+ * @}
+ */
+
+} // esp_modem
diff --git a/components/esp_modem/include/esp_modem_api.h b/components/esp_modem/include/esp_modem_api.h
deleted file mode 100644
index fdc27ab8a9..0000000000
--- a/components/esp_modem/include/esp_modem_api.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#pragma once
-
-#include "esp_err.h"
-#include "generate/esp_modem_command_declare.inc"
-#include "esp_modem_c_api_types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
-        esp_err_t esp_modem_ ## name(esp_modem_dce_t *dce, ##__VA_ARGS__);
-
-DECLARE_ALL_COMMAND_APIS(declares esp_modem_<API>(esp_modem_t *dce, ...);)
-
-#undef ESP_MODEM_DECLARE_DCE_COMMAND
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/components/esp_modem/include/generate/esp_modem_command_declare_helper.inc b/components/esp_modem/include/generate/esp_modem_command_declare_helper.inc
deleted file mode 100644
index fb64bc80d9..0000000000
--- a/components/esp_modem/include/generate/esp_modem_command_declare_helper.inc
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-// Parameters
-// * handle different parameters for C++ and C API
-// * make parameter unique names, so they could be easily referenced and forwarded
-#define INT_IN(param, name) int  _ARG(param, name)
-#ifdef __cplusplus
-#define _ARG(param, name) param
-#include <string>
-#define STRING_IN(param, name) const std::string& _ARG(param, name)
-#define STRING_OUT(param, name) std::string& _ARG(param, name)
-#define BOOL_IN(param, name) const bool  _ARG(param, name)
-#define BOOL_OUT(param, name) bool&  _ARG(param, name)
-#define INT_OUT(param, name) int&  _ARG(param, name)
-#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
-
-#define STRUCT_OUT(struct_name, p1)  struct_name& p1
-#else
-#define _ARG(param, name) name
-#define STRING_IN(param, name) const char* _ARG(param, name)
-#define STRING_OUT(param, name) char* _ARG(param, name)
-#define BOOL_IN(param, name) const bool _ARG(param, name)
-#define BOOL_OUT(param, name) bool* _ARG(param, name)
-#define INT_OUT(param, name) int* _ARG(param, name)
-#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
-#define STRUCT_OUT(struct_name, p1)  esp_modem_ ## struct_name ## _t* p1
-#endif
diff --git a/components/esp_modem/scripts/generate.sh b/components/esp_modem/scripts/generate.sh
new file mode 100755
index 0000000000..34a167b337
--- /dev/null
+++ b/components/esp_modem/scripts/generate.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# Default list of files to process if no arguments are provided
+default_files=("generate/include/cxx_include/esp_modem_command_library.hpp"
+               "generate/include/cxx_include/esp_modem_dce_module.hpp"
+               "generate/include/cxx_include/esp_modem_dce_generic.hpp"
+               "generate/src/esp_modem_modules.cpp"
+               "generate/include/esp_modem_api.h")
+
+# Set the processing directory (defaults to the script's location/..)
+script_dir="$(dirname "$(realpath "$0")")"
+default_processing_dir="$(realpath "$script_dir/..")"
+
+# Parse optional arguments
+if [ -n "$1" ]; then
+  # If one argument is provided, treat it as a single file to process
+  files=("$1")
+else
+  # Use default list if no file argument is provided
+  files=("${default_files[@]}")
+fi
+
+# If a second argument is provided, set it as the processing directory
+if [ -n "$2" ]; then
+  processing_dir="$(realpath "$2")"
+else
+  processing_dir="$default_processing_dir"
+fi
+
+# Process each file
+for file in "${files[@]}"; do
+  # Determine the input and output paths based on processing directory
+  in_file="$processing_dir/$file"
+  out_file="${processing_dir}/${file/generate/command}"
+  current_file_dir="$(dirname "$in_file")"
+
+  # Ensure the output directory exists
+  mkdir -p "$(dirname "$out_file")"
+
+  echo "Processing $in_file"
+
+  # Process the header and includes -- just paste the content (without expanding)
+  sed -n '1,/ESP-MODEM command module starts here/{/ESP-MODEM command module starts here/d;p}' "$in_file" > "$out_file"
+
+  # Determine whether to use clang or clang++ based on file extension
+  if [[ $file == *.cpp || $file == *.hpp ]]; then
+    compiler="clang++ -E -P -CC -xc++"
+  elif [[ $file == *.rst ]]; then
+    compiler="clang -E -P -xc"
+  else
+    compiler="clang -E -P -CC -xc"
+  fi
+
+  # Preprocess everything else to expand command prototypes or implementations
+  sed -n '1,/ESP-MODEM command module starts here/!p' "$in_file" | \
+  $compiler -I"$script_dir" -I"$processing_dir/generate/include" -I"$processing_dir/include" -I"$current_file_dir" -  >> "$out_file"
+  # Add potential footer (typically closing C++ sentinel)
+  sed -n '1,/ESP-MODEM command module ends here/!p' "$in_file" >> "$out_file"
+  if [[ $out_file != *.rst ]]; then
+    astyle --style=otbs --attach-namespaces --attach-classes --indent=spaces=4 --convert-tabs --align-pointer=name --align-reference=name --keep-one-line-statements --pad-header --pad-oper --quiet "$out_file"
+  fi
+done
diff --git a/docs/esp_modem/Doxyfile b/docs/esp_modem/Doxyfile
index 6fe470f93f..c03efcbee7 100644
--- a/docs/esp_modem/Doxyfile
+++ b/docs/esp_modem/Doxyfile
@@ -815,22 +815,22 @@ WARN_LOGFILE           =
 # Note: If this tag is empty the current directory is searched.
 
 INPUT = \
-    $(PROJECT_PATH)/../components/esp_modem/include/esp_modem_api.h \
+    $(PROJECT_PATH)/../components/esp_modem/command/include/esp_modem_api.h \
     $(PROJECT_PATH)/../components/esp_modem/include/esp_modem_c_api_types.h \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_api.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/esp_modem_config.h \
     $(PROJECT_PATH)/../components/esp_modem/include/esp_modem_dce_config.h \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp \
-    $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_command_library.hpp \
-    $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp \
+    $(PROJECT_PATH)/../components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp \
+    $(PROJECT_PATH)/../components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dte.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_netif.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_types.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_terminal.hpp \
     $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_cmux.hpp \
-    $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce.hpp \
-    $(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_api_commands.h \
-    $(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_dce.hpp
+    $(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce_template.hpp \
+    $(PROJECT_PATH)/../docs/esp_modem/command/dce.h \
+    $(PROJECT_PATH)/../docs/esp_modem/command/dce.hpp
 
 
 # The last two are generated
diff --git a/docs/esp_modem/en/api_docs.rst b/docs/esp_modem/en/api_docs.rst
index 3b15b6994e..a7bbf8a6c1 100644
--- a/docs/esp_modem/en/api_docs.rst
+++ b/docs/esp_modem/en/api_docs.rst
@@ -45,7 +45,7 @@ Note that the functions which implement AT commands returning textual values use
 pointer as the return value. The API expects the output data to point to user allocated space of at least
 ``CONFIG_ESP_MODEM_C_API_STR_MAX`` (128 by default) bytes, it also truncates the output data to this size.
 
-.. doxygenfile:: esp_modem_api_commands.h
+.. doxygenfile:: dce.h
 
 .. _api_config:
 
diff --git a/docs/esp_modem/en/cxx_api_docs.rst b/docs/esp_modem/en/cxx_api_docs.rst
index 94fdb94e13..e7e654cc0b 100644
--- a/docs/esp_modem/en/cxx_api_docs.rst
+++ b/docs/esp_modem/en/cxx_api_docs.rst
@@ -33,7 +33,10 @@ Mode switching commands
 Modem commands
 --------------
 
-.. include:: cxx_api_links.rst
+.. include:: ../command/dce.rst
+
+.. doxygenclass:: esp_modem::DCE
+   :members:
 
 .. _cpp_destroy:
 
diff --git a/docs/esp_modem/generate/dce.h b/docs/esp_modem/generate/dce.h
new file mode 100644
index 0000000000..63fd8ebbb0
--- /dev/null
+++ b/docs/esp_modem/generate/dce.h
@@ -0,0 +1,6 @@
+
+//  --- ESP-MODEM command module starts here ---
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) return_type esp_modem_ ## name (ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
+
+#include "esp_modem_command_declare.inc"
diff --git a/docs/esp_modem/generate/dce.hpp b/docs/esp_modem/generate/dce.hpp
new file mode 100644
index 0000000000..5ee2289028
--- /dev/null
+++ b/docs/esp_modem/generate/dce.hpp
@@ -0,0 +1,16 @@
+
+//  --- ESP-MODEM command module starts here ---
+class esp_modem::DCE : public DCE_T<GenericModule> {
+public:
+    using DCE_T<GenericModule>::DCE_T;
+
+#include "esp_modem_command_declare_helper.inc"
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+    return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
+
+
+#include "esp_modem_command_declare.inc"
+
+#undef ESP_MODEM_DECLARE_DCE_COMMAND
+
+};
diff --git a/docs/esp_modem/generate/dce.rst b/docs/esp_modem/generate/dce.rst
new file mode 100644
index 0000000000..062b3bc233
--- /dev/null
+++ b/docs/esp_modem/generate/dce.rst
@@ -0,0 +1,7 @@
+
+//  --- ESP-MODEM command module starts here ---
+
+#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
+* :cpp:func:`esp_modem::DCE::name`
+
+#include "esp_modem_command_declare.inc"
diff --git a/docs/esp_modem/generate_docs b/docs/esp_modem/generate_docs
index 2ca746653c..174acad4bb 100755
--- a/docs/esp_modem/generate_docs
+++ b/docs/esp_modem/generate_docs
@@ -3,15 +3,14 @@
 # Cleanup the generated html
 rm -rf html docs
 
-# Generate C++ API header of the DCE
-cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC  -xc++ -I../../components/esp_modem/include -DGENERATE_DOCS  - | sed -n '1,/DCE command documentation/!p' > en/esp_modem_dce.hpp
+pushd `pwd`
 
-# Generate C API header of the modem_api.h
-cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang -E -P -CC  -xc -I../../components/esp_modem/include -DGENERATE_DOCS  - | sed -n '1,/DCE command documentation/!p' > en/esp_modem_api_commands.h
+cd ../../components/esp_modem/scripts
+./generate.sh ../../docs/esp_modem/generate/dce.rst
+./generate.sh ../../docs/esp_modem/generate/dce.hpp
+./generate.sh ../../docs/esp_modem/generate/dce.h
 
-
-# RST with links to C++ API
-cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang -E -P  -xc -I../../components/esp_modem/include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > en/cxx_api_links.rst
+popd
 
 build-docs --target esp32 --language en