Compare commits

..

22 Commits

Author SHA1 Message Date
9c11003449 Merge pull request #713 from david-cermak/feat/sock_utiis_0.2
[sock-utils]: Bump 0.1 -> 0.2
2024-12-19 11:13:59 +01:00
85a7fc772c bump(sockutls): 0.1.0 -> 0.2.0
0.2.0
Features
- Declare socketpair and gai_strerror via standard headers (b090a3cb)
- Add support for gethostname() (f7c0b756)
2024-12-19 10:57:36 +01:00
b090a3cb69 feat(sockutls): Declare socketpair and gai_strerror via standard headers
Adding a reverse dependency to lwip and define macros, which
enable declarations of socketpair() and gai_strerror() in standard
heders (sys/socket.h and netdb.h)
2024-12-19 10:54:56 +01:00
42cde46c97 Merge pull request #714 from bryghtlabs-richard/chore/websocketTidying
Chore(websocket) tidy up two small issues
2024-12-17 11:35:57 +01:00
beb6e57e5e chore(websocket): align structure members 2024-12-16 16:20:21 -06:00
15d3a01e11 chore(websocket): remove unused client variable 2024-12-16 15:46:43 -06:00
e12ecb8e89 Merge pull request #707 from david-cermak/bugfix/sckutls_gethostname
[sock-utils]: Add support for gethostname()
2024-12-16 16:36:32 +01:00
54271a1b96 Merge pull request #709 from david-cermak/bump/modem_1.3
[modem]: bump 1.2.1 -> 1.3.0
2024-12-12 09:46:27 +01:00
886215032f bump(modem): 1.2.1 -> 1.3.0
1.3.0
Features
- Add mode detection to the example (18f196fa)
- Support for pausing network in C-API (1db83cd1)
- Add support for pausing netif (247f1681, #699)
Bug Fixes
- Minor cleanup of pppos example (5e929902)
- Fix PPP mode detection to accept LCP/conf (c989c6ad)
- Refine mode switch data->command (8b6ea331, #692)
- Detect serial ports properly (0cb59ff8)
- Fix CMUX enter to ignore URC before transition (1284f66d, #669)
2024-12-10 12:48:09 +01:00
269351f41c Merge pull request #700 from david-cermak/feat/modem_pause_network
[modem]: Add support for pausing netif
2024-12-10 12:14:10 +01:00
5e929902c7 fix(modem): Minor cleanup of pppos example
* Use CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT to demonstrate mode
detect
* Use disconnection flag to indicate conneciton issue and gracefully
degrade to command mode
* Remove IDF-verion < v5.0 code
2024-12-10 11:36:10 +01:00
f7c0b7564a feat(sockutls): Add support for gethostname()
Closes https://github.com/espressif/esp-idf/issues/14849
2024-12-09 17:16:59 +01:00
c989c6adae fix(modem): Fix PPP mode detection to accept LCP/conf 2024-12-06 10:12:25 +01:00
18f196fa1e feat(modem): Add mode detection to the example 2024-12-06 09:48:00 +01:00
1db83cd1ca feat(modem): Support for pausing network in C-API
also adds a demo of this feature to pppos client example
2024-12-05 20:16:45 +01:00
247f1681e8 feat(modem): Add support for pausing netif
Closes https://github.com/espressif/esp-protocols/issues/699
2024-12-05 20:16:41 +01:00
32387f7e39 Merge pull request #702 from david-cermak/fix/modem_data_cmd_mode_refine
[modem]: Refine data -> command transition
2024-12-04 15:42:43 +01:00
dbc3ea6809 fix(common): Export IDF environment in bash shell
To avoid issues with IDF_PATH/export with the latest tools
2024-12-04 14:44:43 +01:00
8b6ea3311a fix(modem): Refine mode switch data->command
* netif.stop() moved after setting the transition callback
* send PPP escape sequence if enabled before waiting for transition to
complete
* add newline character before sync() command (after the "+++")

Closes https://github.com/espressif/esp-protocols/issues/692
2024-12-04 14:39:01 +01:00
8e55b93b59 Merge pull request #703 from david-cermak/fix/modem_cmux_data_before_switch
[modem]: CMUX: ignore URC before entering CMUX
2024-12-04 08:18:54 +01:00
0cb59ff80d fix(modem): Detect serial ports properly 2024-11-29 18:27:26 +01:00
1284f66d58 fix(modem): Fix CMUX enter to ignore URC before transition
Closes https://github.com/espressif/esp-protocols/issues/669
2024-11-29 17:32:35 +01:00
34 changed files with 485 additions and 49 deletions

View File

@ -24,6 +24,7 @@ jobs:
chmod +x clang-tidy-sarif
curl -sSL https://raw.githubusercontent.com/espressif/idf-extra-components/master/.github/filter_sarif.py -o filter_sarif.py
- name: Install pyclang
shell: bash
run: |
. ${IDF_PATH}/export.sh
pip install pyclang~=0.2.0

View File

@ -80,7 +80,12 @@ jobs:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
env:
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
run: |
python -m pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
python -m venv .venv
source .venv/bin/activate
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
cd ${{ env.TEST_DIR }}
python -m pytest --log-cli-level DEBUG --target=${{ matrix.idf_target }}

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(modem): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_modem
tag_format: modem-v$version
version: 1.2.1
version: 1.3.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,21 @@
# Changelog
## [1.3.0](https://github.com/espressif/esp-protocols/commits/modem-v1.3.0)
### Features
- Add mode detection to the example ([18f196fa](https://github.com/espressif/esp-protocols/commit/18f196fa))
- Support for pausing network in C-API ([1db83cd1](https://github.com/espressif/esp-protocols/commit/1db83cd1))
- Add support for pausing netif ([247f1681](https://github.com/espressif/esp-protocols/commit/247f1681), [#699](https://github.com/espressif/esp-protocols/issues/699))
### Bug Fixes
- Minor cleanup of pppos example ([5e929902](https://github.com/espressif/esp-protocols/commit/5e929902))
- Fix PPP mode detection to accept LCP/conf ([c989c6ad](https://github.com/espressif/esp-protocols/commit/c989c6ad))
- Refine mode switch data->command ([8b6ea331](https://github.com/espressif/esp-protocols/commit/8b6ea331), [#692](https://github.com/espressif/esp-protocols/issues/692))
- Detect serial ports properly ([0cb59ff8](https://github.com/espressif/esp-protocols/commit/0cb59ff8))
- Fix CMUX enter to ignore URC before transition ([1284f66d](https://github.com/espressif/esp-protocols/commit/1284f66d), [#669](https://github.com/espressif/esp-protocols/issues/669))
## [1.2.1](https://github.com/espressif/esp-protocols/commits/modem-v1.2.1)
### Bug Fixes

View File

@ -76,4 +76,20 @@ menu "esp-modem"
help
If enabled, APIs to add URC handler are available
config ESP_MODEM_PPP_ESCAPE_BEFORE_EXIT
bool "Send escape sequence when switching PPP -> CMD"
default n
help
If enabled, the library sends a PPP escape ("+++" command)
to switch to command mode. This make switching from PPP to CMD
mode more robust for some devices (e.g. Quectel), but might cause
trouble for other devices (e.g. SIMCOM).
config ESP_MODEM_ADD_DEBUG_LOGS
bool "Add UART Tx/Rx logs"
default n
help
If enabled, the library dumps all transmitted and received data.
This option is only used for debugging.
endmenu

View File

@ -385,6 +385,17 @@ extern "C" void app_main(void)
return 0;
});
#endif
const ConsoleCommand PauseNetwork("pause_net", "toggle network pause", no_args, [&](ConsoleCommand * c) {
static int cnt = 0;
if (++cnt % 2) {
ESP_LOGI(TAG, "Pausing netif");
dce->pause_netif(true);
} else {
ESP_LOGI(TAG, "Unpausing netif");
dce->pause_netif(false);
}
return 0;
});
const struct SetApn {
SetApn(): apn(STR1, nullptr, nullptr, "<apn>", "APN (Access Point Name)") {}

View File

@ -201,4 +201,23 @@ menu "Example Configuration"
help
MQTT data message, which we publish and expect to receive.
config EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
bool "Demonstrate netif pause"
default n
help
Set this to true to demonstrate network pausing.
If enabled, the example waits for an MQTT data, then temporarily
drops network to check signal quality, resumes networking and
publishes another MQTT message.
Connection to the MQTT broker should be kept.
config EXAMPLE_DETECT_MODE_BEFORE_CONNECT
bool "Detect mode before connect"
default n
help
Set this to true to demonstrate mode auto-detection.
If enabled, the example tries to recognize the actual mode.
If mode is detected correctly and it is not a command mode,
then the example switches to command mode.
endmenu

View File

@ -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: Unlicense OR CC0-1.0
*/
@ -34,6 +34,7 @@
static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int DISCONNECT_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
@ -55,6 +56,7 @@ static void usb_terminal_error_handler(esp_modem_terminal_error_t err)
}
#define CHECK_USB_DISCONNECTION(event_group) \
if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED_BIT) { \
ESP_LOGE(TAG, "USB_DISCONNECTED_BIT destroying modem dce"); \
esp_modem_destroy(dce); \
continue; \
}
@ -140,6 +142,7 @@ static void on_ip_event(void *arg, esp_event_base_t event_base,
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
xEventGroupSetBits(event_group, DISCONNECT_BIT);
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
@ -158,6 +161,7 @@ void app_main(void)
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
/* Configure the PPP netif */
esp_err_t err;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
@ -205,7 +209,7 @@ void app_main(void)
#endif
assert(dce);
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
esp_err_t err = esp_modem_set_flow_control(dce, 2, 2); //2/2 means HW Flow Control.
err = esp_modem_set_flow_control(dce, 2, 2); //2/2 means HW Flow Control.
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
return;
@ -246,7 +250,27 @@ void app_main(void)
#error Invalid serial connection to modem.
#endif
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT);
#if CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DETECT) failed with %d", err);
return;
}
esp_modem_dce_mode_t mode = esp_modem_get_mode(dce);
ESP_LOGI(TAG, "Mode detection completed: current mode is: %d", mode);
if (mode == ESP_MODEM_MODE_DATA) { // set back to command mode
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);
return;
}
ESP_LOGI(TAG, "Command mode restored");
}
#endif // CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
/* Run the modem demo app */
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
@ -262,7 +286,7 @@ void app_main(void)
#endif
int rssi, ber;
esp_err_t err = esp_modem_get_signal_quality(dce, &rssi, &ber);
err = esp_modem_get_signal_quality(dce, &rssi, &ber);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d %s", err, esp_err_to_name(err));
return;
@ -301,22 +325,41 @@ void app_main(void)
}
/* Wait for IP address */
ESP_LOGI(TAG, "Waiting for IP address");
xEventGroupWaitBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
xEventGroupWaitBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT, pdFALSE, pdFALSE,
pdMS_TO_TICKS(60000));
CHECK_USB_DISCONNECTION(event_group);
if ((xEventGroupGetBits(event_group) & CONNECT_BIT) != CONNECT_BIT) {
ESP_LOGW(TAG, "Modem not connected, switching back to the command mode");
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);
return;
}
ESP_LOGI(TAG, "Command mode restored");
return;
}
/* Config MQTT */
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
esp_mqtt_client_config_t mqtt_config = {
.broker.address.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI,
};
#else
esp_mqtt_client_config_t mqtt_config = {
.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI,
};
#endif
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
#if CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
esp_modem_pause_net(dce, true);
err = esp_modem_get_signal_quality(dce, &rssi, &ber);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d", err);
return;
}
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
esp_modem_pause_net(dce, false);
esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
#endif // CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
ESP_LOGI(TAG, "Waiting for MQTT data");
xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
CHECK_USB_DISCONNECTION(event_group);

View File

@ -12,7 +12,7 @@ def test_pppos_connect(dut):
4. checks that the client cleanly disconnects
"""
# Check the sequence of connecting, publishing, disconnecting
dut.expect('Modem Connect to PPP Server')
dut.expect('Modem Connect to PPP Server', timeout=90)
# Check for MQTT connection and the data event
dut.expect('MQTT_EVENT_CONNECTED')
dut.expect('MQTT_EVENT_DATA')

View File

@ -11,5 +11,7 @@ CONFIG_EXAMPLE_MODEM_DEVICE_SIM800=y
CONFIG_EXAMPLE_MODEM_DEVICE_BG96=n
CONFIG_EXAMPLE_MODEM_PPP_APN="lpwa.vodafone.com"
CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client"
CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP32_PANIC_PRINT_HALT=y
CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT=y

View File

@ -16,3 +16,4 @@ CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_EXAMPLE_CLOSE_CMUX_AT_END=y
CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client"
CONFIG_BROKER_URI="mqtt://mqtt.eclipseprojects.io"

View File

@ -1,4 +1,4 @@
version: "1.2.1"
version: "1.3.0"
description: Library for communicating with cellular modems in command and data modes
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
issues: https://github.com/espressif/esp-protocols/issues

View File

@ -91,6 +91,11 @@ public:
return mode.set(dte.get(), device.get(), netif, m);
}
modem_mode get_mode()
{
return mode.get();
}
bool recover()
{
return dte->recover();
@ -103,6 +108,29 @@ public:
}
#endif
/**
* @brief Pauses/Unpauses network temporarily
* @param do_pause true to pause, false to unpause
* @param force true to ignore command failures and continue
* @return command_result of the underlying commands
*/
command_result pause_netif(bool do_pause, bool force = false, int delay = 1000)
{
command_result result;
if (do_pause) {
netif.pause();
Task::Delay(delay); // Mandatory 1s pause before
dte->set_command_callbacks();
result = device->set_command_mode();
} else {
result = device->resume_data_mode();
if (result == command_result::OK || force) {
netif.resume();
}
}
return result;
}
protected:
std::shared_ptr<DTE> dte;
std::shared_ptr<SpecificModule> device;

View File

@ -77,7 +77,9 @@ public:
if (set_command_mode() == command_result::OK) {
return true;
}
Task::Delay(1000); // Mandatory 1s pause after escape
// send a newline to delimit the escape from the upcoming sync command
uint8_t delim = '\n';
dte->write(&delim, 1);
if (sync() == command_result::OK) {
return true;
}

View File

@ -145,6 +145,12 @@ public:
*/
bool recover();
/**
* @brief Set internal command callbacks to the underlying terminal.
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
*/
void set_command_callbacks();
protected:
/**
* @brief Allows for locking the DTE
@ -204,12 +210,6 @@ private:
} inflatable;
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
/**
* @brief Set internal command callbacks to the underlying terminal.
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
*/
void set_command_callbacks();
/**
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
*/

View File

@ -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
*/
@ -54,6 +54,16 @@ public:
*/
void stop();
/**
* @brief Pause the network interface
*/
void pause();
/**
* @brief Resume the network interface
*/
void resume();
void receive(uint8_t *data, size_t len);
private:

View File

@ -45,6 +45,8 @@ typedef enum esp_modem_dce_mode {
ESP_MODEM_MODE_CMUX_MANUAL_SWAP, /**< Swap terminals in CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_DATA, /**< Set DATA mode in CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_COMMAND, /**< Set COMMAND mode in CMUX manual mode */
ESP_MODEM_MODE_DETECT, /**< Detect the mode and resume it (if sucessfully detected) */
ESP_MODEM_MODE_UNDEF,
} esp_modem_dce_mode_t;
/**
@ -160,6 +162,18 @@ esp_err_t esp_modem_set_apn(esp_modem_dce_t *dce, const char *apn);
esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce, esp_err_t(*got_line_cb)(uint8_t *data, size_t len));
#endif
/**
* @brief This API provides support for temporarily pausing networking in order
* to send/receive AT commands and resume networking afterwards.
* @note This function does not switch modes, the modem is still in data mode.
*
* @param dce Modem DCE handle
* @param pause true to pause the network interface, false to resume networking
* @return ESP_OK on success
*/
esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce, bool pause);
esp_modem_dce_mode_t esp_modem_get_mode(esp_modem_dce_t *dce);
/**
* @}
*/

View File

@ -95,12 +95,43 @@ extern "C" esp_err_t esp_modem_sync(esp_modem_dce_t *dce_wrap)
return command_response_to_esp_err(dce_wrap->dce->sync());
}
extern "C" esp_modem_dce_mode_t esp_modem_get_mode(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_MODEM_MODE_UNDEF;
}
auto mode = dce_wrap->dce->get_mode();
switch (mode) {
default:
case modem_mode::UNDEF:
return ESP_MODEM_MODE_UNDEF;
case modem_mode::COMMAND_MODE:
return ESP_MODEM_MODE_COMMAND;
case modem_mode::DATA_MODE:
return ESP_MODEM_MODE_DATA;
case modem_mode::CMUX_MODE:
return ESP_MODEM_MODE_CMUX;
case modem_mode::CMUX_MANUAL_MODE:
return ESP_MODEM_MODE_CMUX_MANUAL;
case modem_mode::CMUX_MANUAL_EXIT:
return ESP_MODEM_MODE_CMUX_MANUAL_EXIT;
case modem_mode::CMUX_MANUAL_DATA:
return ESP_MODEM_MODE_CMUX_MANUAL_DATA;
case modem_mode::CMUX_MANUAL_COMMAND:
return ESP_MODEM_MODE_CMUX_MANUAL_COMMAND;
case modem_mode::CMUX_MANUAL_SWAP:
return ESP_MODEM_MODE_CMUX_MANUAL_SWAP;
}
}
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce_mode_t mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
switch (mode) {
case ESP_MODEM_MODE_UNDEF:
return dce_wrap->dce->set_mode(modem_mode::UNDEF) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_DATA:
return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_COMMAND:
@ -117,6 +148,8 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_DETECT:
return dce_wrap->dce->set_mode(modem_mode::AUTODETECT) ? ESP_OK : ESP_FAIL;
}
return ESP_ERR_NOT_SUPPORTED;
}
@ -475,3 +508,19 @@ extern "C" esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce_wrap, esp_err_t(*got
return ESP_OK;
}
#endif
extern "C" esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce_wrap, bool pause)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->pause_netif(pause));
}
extern "C" esp_err_t esp_modem_hang_up(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->hang_up());
}

View File

@ -123,7 +123,12 @@ bool CMux::data_available(uint8_t *data, size_t len)
{
if (data && (type & FT_UIH) == FT_UIH && len > 0 && dlci > 0) { // valid payload on a virtual term
int virtual_term = dlci - 1;
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
if (virtual_term < MAX_TERMINALS_NUM) {
if (read_cb[virtual_term] == nullptr) {
// ignore all virtual terminal's data before we completely establish CMUX
ESP_LOG_BUFFER_HEXDUMP("CMUX Rx before init", data, len, ESP_LOG_DEBUG);
return true;
}
// Post partial data (or defragment to post on CMUX footer)
#ifdef DEFRAGMENT_CMUX_PAYLOAD
if (payload_start == nullptr) {
@ -142,7 +147,11 @@ bool CMux::data_available(uint8_t *data, size_t len)
sabm_ack = dlci;
} else if (data == nullptr && dlci > 0) {
int virtual_term = dlci - 1;
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
if (virtual_term < MAX_TERMINALS_NUM) {
if (read_cb[virtual_term] == nullptr) {
// silently ignore this CMUX frame (not finished entering CMUX, yet)
return true;
}
#ifdef DEFRAGMENT_CMUX_PAYLOAD
read_cb[virtual_term](payload_start, total_payload_size);
#endif

View File

@ -18,7 +18,6 @@ namespace transitions {
static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
{
netif.stop();
auto signal = std::make_shared<SignalGroup>();
std::weak_ptr<SignalGroup> weak_signal = signal;
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
@ -32,7 +31,7 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
if (memchr(data, '\n', len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED", "OK"});
std::string_view response((char *) data, len);
for (auto &it : pass)
if (response.find(it) != std::string::npos) {
@ -44,8 +43,14 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
}
return false;
});
netif.stop();
netif.wait_until_ppp_exits();
if (!signal->wait(1, 2000)) {
#ifdef ESP_MODEM_PPP_ESCAPE_BEFORE_EXIT
std::array<uint8_t, 3> ppp_escape = {'+', '+', '+'};
dte.write(ppp_escape.data(), ppp_escape.size());
#endif
if (!signal->wait(1, 2000)) { // wait for any of the disconnection messages
// if no reply -> set device to command mode
dte.set_read_cb(nullptr);
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
return false;
@ -323,12 +328,19 @@ modem_mode DCE_Mode::guess_unsafe(DTE *dte, bool with_cmux)
if (reply_pos >= sizeof(probe::ppp::lcp_echo_reply_head)) {
// check for initial 2 bytes
auto *ptr = static_cast<uint8_t *>(memmem(reply, reply_pos, probe::ppp::lcp_echo_reply_head.data(), 2));
// and check the other two bytes for protocol ID: LCP
// and check the other two bytes for protocol ID:
// * either LCP reply
if (ptr && ptr[3] == probe::ppp::lcp_echo_reply_head[3] && ptr[4] == probe::ppp::lcp_echo_reply_head[4]) {
if (auto signal = weak_signal.lock()) {
signal->set(probe::ppp::mode);
}
}
// * or LCP conf request
if (ptr && ptr[3] == probe::ppp::lcp_echo_request[3] && ptr[4] == probe::ppp::lcp_echo_request[4]) {
if (auto signal = weak_signal.lock()) {
signal->set(probe::ppp::mode);
}
}
}
if (reply_pos >= 4 && memmem(reply, reply_pos, probe::cmd::reply, sizeof(probe::cmd::reply))) {
if (reply[0] != 0xf9) { // double check that the reply is not wrapped in CMUX headers

View File

@ -99,6 +99,20 @@ void Netif::stop()
signal.clear(PPP_STARTED);
}
void Netif::resume()
{
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
receive(data, len);
return true;
});
signal.set(PPP_STARTED);
}
void Netif::pause()
{
signal.clear(PPP_STARTED);
}
Netif::~Netif()
{
if (signal.is_any(PPP_STARTED)) {

View File

@ -52,7 +52,6 @@ void Netif::start()
void Netif::stop()
{
ppp_dte->set_read_cb(nullptr);
signal.clear(PPP_STARTED);
}

View File

@ -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
*/
@ -176,13 +176,20 @@ int UartTerminal::read(uint8_t *data, size_t len)
uart_get_buffered_data_len(uart.port, &length);
length = std::min(len, length);
if (length > 0) {
return uart_read_bytes(uart.port, data, length, portMAX_DELAY);
int read_len = uart_read_bytes(uart.port, data, length, portMAX_DELAY);
#if CONFIG_ESP_MODEM_ADD_DEBUG_LOGS
ESP_LOG_BUFFER_HEXDUMP("uart-rx", data, read_len, ESP_LOG_DEBUG);
#endif
return read_len;
}
return 0;
}
int UartTerminal::write(uint8_t *data, size_t len)
{
#if CONFIG_ESP_MODEM_ADD_DEBUG_LOGS
ESP_LOG_BUFFER_HEXDUMP("uart-tx", data, len, ESP_LOG_DEBUG);
#endif
return uart_write_bytes_compat(uart.port, data, len);
}

View File

@ -9,6 +9,20 @@ from threading import Event, Thread
import netifaces
def is_esp32(port):
"""
Check if the given port is connected to an ESP32 using esptool.
"""
try:
result = subprocess.run(
['esptool.py', '--port', port, 'chip_id'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True
)
return 'ESP32' in result.stdout
except subprocess.CalledProcessError:
return False
def run_server(server_stop, port, server_ip, client_ip, auth, auth_user, auth_password):
print('Starting PPP server on port: {}'.format(port))
try:
@ -66,13 +80,27 @@ def test_examples_protocol_pppos_connect(dut):
)
raise
# the PPP test env uses two ttyUSB's: one for ESP32 board, another one for ppp server
# use the other port for PPP server than the DUT/ESP
port = '/dev/ttyUSB0' if dut.serial.port == '/dev/ttyUSB1' else '/dev/ttyUSB1'
# the PPP test env uses three ttyUSB's: two for ESP32 board and another one for the ppp server
# we need to detect the server_port (for PPPD)
server_port = None
for i in ['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2']:
if i == dut.serial.port:
print(f'DUT port: {i}')
elif is_esp32(i):
print(f'Some other ESP32: {i}')
else:
print(f'Port for PPPD: {i}')
server_port = i
if server_port is None:
print(
'ENV_TEST_FAILURE: Cannot locate PPPD port'
)
raise
# Start the PPP server
server_stop = Event()
t = Thread(target=run_server,
args=(server_stop, port, server_ip, client_ip, auth, auth_user, auth_password))
args=(server_stop, server_port, server_ip, client_ip, auth, auth_user, auth_password))
t.start()
try:
ppp_server_timeout = time.time() + 30

View File

@ -122,12 +122,11 @@ struct esp_websocket_client {
uint64_t ping_tick_ms;
uint64_t pingpong_tick_ms;
int wait_timeout_ms;
int auto_reconnect;
bool run;
bool wait_for_pong_resp;
bool selected_for_destroying;
EventGroupHandle_t status_bits;
SemaphoreHandle_t lock;
SemaphoreHandle_t lock;
size_t errormsg_size;
char *errormsg_buffer;
char *rx_buffer;

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(sockutls): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py sock_utils
tag_format: sock_utils-v$version
version: 0.1.0
version: 0.2.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,12 @@
# Changelog
## [0.2.0](https://github.com/espressif/esp-protocols/commits/sock_utils-v0.2.0)
### Features
- Declare socketpair and gai_strerror via standard headers ([b090a3cb](https://github.com/espressif/esp-protocols/commit/b090a3cb))
- Add support for gethostname() ([f7c0b756](https://github.com/espressif/esp-protocols/commit/f7c0b756))
## [0.1.0](https://github.com/espressif/esp-protocols/commits/sock_utils-v0.1.0)
### Features

View File

@ -2,5 +2,15 @@ idf_component_register(SRCS "src/getnameinfo.c"
"src/ifaddrs.c"
"src/gai_strerror.c"
"src/socketpair.c"
"src/gethostname.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES lwip esp_netif)
# To support declarations from standard headers in lwip component
# - socket pair from lwip/sockets.h
# - gai_strerror from lwip/netdb.h
# also need to make lwip depend on the sock_utils lib
idf_component_get_property(lwip lwip COMPONENT_LIB)
target_compile_definitions(${lwip} PUBLIC LWIP_SOCKET_HAS_SOCKETPAIR=1)
target_compile_definitions(${lwip} PUBLIC LWIP_NETDB_HAS_GAI_STRERROR=1)
target_link_libraries(${lwip} PUBLIC ${COMPONENT_LIB})

View File

@ -6,12 +6,17 @@ This component provides simplified implementations of common socket-related util
## Supported Functions
| API | Description | Limitations |
|------------------|-------------------------------------------------------------|-------------------------------------------------------------------|
| `ifaddrs()` | Retrieves interface addresses using `esp_netif` | IPv4 addresses only |
| `socketpair()` | Creates a pair of connected sockets using `lwIP` loopback stream sockets | IPv4 sockets only |
| `pipe()` | Wraps `socketpair()` to provide unidirectional pipe-like functionality | Uses bidirectional sockets in place of true pipes |
| `getnameinfo()` | Converts IP addresses to human-readable form using `lwIP`'s `inet_ntop()` | IPv4 only; supports `NI_NUMERICHOST` and `NI_NUMERICSERV` flags only |
| `gai_strerror()` | Returns error code as a string | Simple numeric string representation only |
| API | Description | Limitations | Declared in |
|--------------------|-------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------|
| `ifaddrs()` | Retrieves interface addresses using `esp_netif` | IPv4 addresses only | `ifaddrs.h` |
| `socketpair()` *) | Creates a pair of connected sockets using `lwIP` loopback stream sockets | IPv4 sockets only | `socketpair.h`, `sys/socket.h` **) |
| `pipe()` *) | Wraps `socketpair()` to provide unidirectional pipe-like functionality | Uses bidirectional sockets in place of true pipes | `socketpair.h`, `unistd.h` ***) |
| `getnameinfo()` | Converts IP addresses to human-readable form using `lwIP`'s `inet_ntop()` | IPv4 only; supports `NI_NUMERICHOST` and `NI_NUMERICSERV` flags only | `getnameinfo.h`, `netdb.h` in ESP-IDF |
| `gai_strerror()` | Returns error code as a string | Simple numeric string representation only | `gai_strerror.h`, `netdb.h` **) |
| `gethostname()` | Returns lwip netif hostname | Not a system-wide hostname, but interface specific hostname | `gethostname.h`, `unistd.h` in ESP-IDF |
**Note**: `socketpair()` and `pipe()` are built on top of `lwIP` TCP sockets, inheriting the same characteristics. For instance, the maximum transmit buffer size is based on the `TCP_SND_BUF` setting.
**Notes**:
- **`*)`** `socketpair()` and `pipe()` are built on top of `lwIP` TCP sockets, inheriting the same characteristics. For instance, the maximum transmit buffer size is based on the `TCP_SND_BUF` setting.
- **`**)`** `socketpair()` and `gai_strerror()` are declared in sock_utils header files, the declaration is propagated to ESP-IDF from v5.5 to the official header files. If you're using older IDF version, you need to manually pre-include related header files from the sock_utils public include directory.
- **`***)`** `pipe()` is declared in compiler's `sys/unistd.h`.

View File

@ -1,4 +1,4 @@
version: 0.1.0
version: 0.2.0
description: The component provides helper implementation of common system/socket utilities
url: https://github.com/espressif/esp-protocols/tree/master/components/sock_utils
dependencies:

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <unistd.h>
#include "sdkconfig.h"
#ifdef CONFIG_IDF_TARGET_LINUX
// namespace with esp_ on linux to avoid conflict of symbols
#define gethostname esp_gethostname
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Retrieves the hostname of the device.
*
* This function provides the hostname associated with the network interface.
* Unlike the standard behavior where the hostname represents a system-wide name,
* this implementation returns lwip netif hostname (used as a hostname in DHCP packets)
*
* @param[out] name A pointer to a buffer where the hostname will be stored.
* The buffer must be allocated by the caller.
* @param[in] len The size of the buffer pointed to by @p name. The hostname,
* including the null-terminator, must fit within this size.
*
* @return
* - 0 on success
* - -1 on error, with `errno` set to indicate the error:
* - `EINVAL`: Invalid argument, name is NULL, or hostname is too long
*
* @note This implementation retrieves the hostname associated with the network
* interface using the `esp_netif_get_hostname()` function, which in turn
* returns lwip netif hostname used in DHCP packets if LWIP_NETIF_HOSTNAME=1 (hardcoded)
* in ESP-IDF lwip port.
* As there could be multiple network interfaces in the system, the logic tries
* to find the default (active) netif first, then it looks for any (inactive) netif
* with highest route priority. If none of the above found or esp_netif_get_hostname() fails
* for the selected interface, this API returns the default value of `CONFIG_LWIP_LOCAL_HOSTNAME`,
* the local hostname from lwip component configuration menu.
*/
int gethostname(char *name, size_t len);
#ifdef __cplusplus
}
#endif

View File

@ -32,3 +32,10 @@
#ifndef AF_UNIX
#define AF_UNIX 1
#endif
#ifndef PF_LOCAL
/*
* In POSIX, AF_UNIX and PF_LOCAL are essentially synonymous.
*/
#define PF_LOCAL AF_UNIX
#endif

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "gethostname.h"
#include "esp_netif.h"
#include "errno.h"
#include "esp_log.h"
static bool highest_prio_netif(esp_netif_t *netif, void *ctx)
{
esp_netif_t **highest_so_far = ctx;
if (esp_netif_get_route_prio(netif) > esp_netif_get_route_prio(*highest_so_far)) {
*highest_so_far = netif;
}
return false; // go over the entire list to find the netif with the highest route-prio
}
int gethostname(char *name, size_t len)
{
if (name == NULL) {
errno = EINVAL;
return -1;
}
const char *netif_hostname = CONFIG_LWIP_LOCAL_HOSTNAME; // default value from Kconfig
// Find the default netif
esp_netif_t *default_netif = esp_netif_get_default_netif();
if (default_netif == NULL) { // if no netif is active/up -> find the highest prio netif
esp_netif_find_if(highest_prio_netif, &default_netif);
}
// now the `default_netif` could be NULL and/or the esp_netif_get_hostname() could fail
// but we ignore the return code, as if it fails, the `netif_hostname` still holds the default value
esp_netif_get_hostname(default_netif, &netif_hostname);
size_t hostname_len;
if (netif_hostname == NULL || len < (hostname_len = strlen(netif_hostname) + 1)) { // including the NULL terminator
errno = EINVAL;
return -1;
}
memcpy(name, netif_hostname, hostname_len);
return 0;
}

View File

@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "gethostname.h"
#include "ifaddrs.h"
#include "esp_netif.h"
#include "esp_event.h"
@ -148,6 +149,30 @@ TEST_CASE("gai_strerror()", "[sock_utils]")
CHECK(str_error != NULL);
}
TEST_CASE("gethostname()", "[sock_utils]")
{
const char *test_netif_name = "station";
char hostname[32];
int ret;
// expect failure
ret = gethostname(hostname, strlen(CONFIG_LWIP_LOCAL_HOSTNAME) - 1);
CHECK(ret == -1);
// happy flow with the default name
ret = gethostname(hostname, sizeof(hostname));
CHECK(ret == 0);
CHECK(strcmp(hostname, CONFIG_LWIP_LOCAL_HOSTNAME) == 0);
// happy flow with the netif name
esp_netif_t *esp_netif = create_test_netif(test_netif_name, 1);
REQUIRE(esp_netif != NULL);
CHECK(esp_netif_set_hostname(esp_netif, test_netif_name) == ESP_OK);
ret = gethostname(hostname, sizeof(hostname));
CHECK(ret == 0);
CHECK(strcmp(hostname, test_netif_name) == 0);
esp_netif_destroy(esp_netif);
}
extern "C" void app_main(void)
{