espressif / esp-protocols

Collection of ESP-IDF components related to networking protocols
195 stars 133 forks source link

esp_modem_at throwing timeout error (IDFGH-8851) #183

Closed StarOfSlytherin closed 1 year ago

StarOfSlytherin commented 1 year ago

esp_modem_at() is throwing ESP_ERR_TIMEOUT when used with CMUX.

Here's my code:

/*
 * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
/* PPPoS Client Example

   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 <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem_api.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "driver/gpio.h"

#define BUF_SIZE 1024
char at_response[BUF_SIZE];

esp_modem_dce_t *dce;

static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int GOT_DATA_BIT = BIT2;

esp_mqtt_client_handle_t client;

static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        msg_id = esp_mqtt_client_subscribe(client, "/topic/esp-pppos/#", 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        esp_mqtt_client_reconnect(client);
        break;
    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "{\"sentFrom\":\"ESP32 via PPPoS\"}", 0, 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        xEventGroupSetBits(event_group, GOT_DATA_BIT);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        break;
    default:
        ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
        break;
    }
}

static void on_ppp_changed(void *arg, esp_event_base_t event_base,
                           int32_t event_id, void *event_data)
{
    ESP_LOGI(TAG, "PPP state changed event %d", event_id);
    if (event_id == NETIF_PPP_ERRORUSER)
    {
        /* User interrupted event from esp-netif */
        esp_netif_t *netif = event_data;
        ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
    }
}

static void on_ip_event(void *arg, esp_event_base_t event_base,
                        int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "IP event! %d", event_id);
    if (event_id == IP_EVENT_PPP_GOT_IP)
    {
        esp_netif_dns_info_t dns_info;

        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        esp_netif_t *netif = event->esp_netif;

        ESP_LOGI(TAG, "Modem Connect to PPP Server");
        ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
        ESP_LOGI(TAG, "IP          : " IPSTR, IP2STR(&event->ip_info.ip));
        ESP_LOGI(TAG, "Netmask     : " IPSTR, IP2STR(&event->ip_info.netmask));
        ESP_LOGI(TAG, "Gateway     : " IPSTR, IP2STR(&event->ip_info.gw));
        esp_netif_get_dns_info(netif, 0, &dns_info);
        ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
        esp_netif_get_dns_info(netif, 1, &dns_info);
        ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
        ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
        xEventGroupSetBits(event_group, CONNECT_BIT);

        ESP_LOGI(TAG, "GOT ip event!!!");
    }
    else if (event_id == IP_EVENT_PPP_LOST_IP)
    {
        ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
    }
    else if (event_id == IP_EVENT_GOT_IP6)
    {
        ESP_LOGI(TAG, "GOT IPv6 event!");

        ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
        ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
    }
}

esp_err_t get_tower_info()
{
    const char *AT_CMD = "AT+CPSI?";
    esp_err_t err = esp_modem_at(dce, AT_CMD, at_response, 100);
    if (err == ESP_OK)
    {
        ESP_LOGI(TAG, "AT Command Test: %s", AT_CMD);
        ESP_LOGI(TAG, "Response: %s", at_response);

        int i = 0;
        char *token;
        char *gsmData[20];
        token = strtok(at_response, ",");
        while (token != NULL)
        {
            printf("%d) %s\n", i, token);
            gsmData[i++] = token;
            token = strtok(NULL, ",");
        }
        ESP_LOGI(TAG, "MCC-MNC  :%s", gsmData[2]);
        ESP_LOGI(TAG, "TAC      :%s", gsmData[3]);
        ESP_LOGI(TAG, "Band     :%s", gsmData[6]);
    }
    else
    {
        ESP_LOGE(TAG, "Failed to run esp_modem_at(dce, %s) with error: %s", AT_CMD, esp_err_to_name(err));
    }
    return err;
}

void app_main(void)
{
    /* Turn ON SIM7600E */
    gpio_reset_pin(GPIO_NUM_12);
    gpio_set_direction(GPIO_NUM_12, GPIO_MODE_OUTPUT);
    gpio_set_level(GPIO_NUM_12, 1);
    vTaskDelay(pdMS_TO_TICKS(1000));
    gpio_set_level(GPIO_NUM_12, 0);

    /* Init and register system/core components */
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));

    /* Configure the PPP netif */
    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);
    assert(esp_netif);

    event_group = xEventGroupCreate();

    /* Configure the DTE */
    esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
    /* setup UART specific configuration based on kconfig options */
    dte_config.uart_config.tx_io_num = 32;
    dte_config.uart_config.rx_io_num = 33;
    dte_config.uart_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
    dte_config.uart_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
    dte_config.uart_config.flow_control = ESP_MODEM_FLOW_CONTROL_NONE;
    dte_config.uart_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
    dte_config.uart_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
    dte_config.uart_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
    dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE;
    dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
    dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;

    ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
    dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM7600, &dte_config, &dce_config, esp_netif);

    assert(dce);

    while (esp_modem_sync(dce) != ESP_OK)
    {
        ESP_LOGI(TAG, "Waiting for Modem Sync");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT);

    int rssi, ber;
    esp_err_t 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;
    }
    ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);

    err = esp_modem_set_mode(dce, ESP_MODEM_MODE_CMUX);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DATA) failed with %d", err);
        return;
    }
    /* Wait for IP address */
    ESP_LOGI(TAG, "Waiting for IP address");
    xEventGroupWaitBits(event_group, CONNECT_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

    /* Config MQTT */
    esp_mqtt_client_config_t mqtt_config = {
        .uri = "mqtt://test.mosquitto.org",
        .port = 1883,
    };

    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);
    ESP_LOGI(TAG, "Waiting for MQTT data");
    xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

    while (true)
    {
        esp_mqtt_client_publish(client, "/topic/esp-pppos/cmux", "{\"sentFrom\":\"ESP32 via PPPoS CMUX\"}", 0, 0, 0);
        vTaskDelay(pdMS_TO_TICKS(5000));
        get_tower_info();
    }
}

Output:

I (00:01:27.471) pppos_example: MQTT_EVENT_DATA
TOPIC=/topic/esp-pppos/cmux
DATA={"sentFrom":"ESP32 via PPPoS CMUX"}
E (00:01:32.282) pppos_example: Failed to run esp_modem_at(dce, AT+CPSI?) with error: ESP_ERR_TIMEOUT
I (00:01:32.591) pppos_example: MQTT_EVENT_DATA
TOPIC=/topic/esp-pppos/cmux
DATA={"sentFrom":"ESP32 via PPPoS CMUX"}
E (00:01:37.382) pppos_example: Failed to run esp_modem_at(dce, AT+CPSI?) with error: ESP_ERR_TIMEOUT
I (00:01:37.631) pppos_example: MQTT_EVENT_DATA
TOPIC=/topic/esp-pppos/cmux
DATA={"sentFrom":"ESP32 via PPPoS CMUX"}

When I check the SIM7600's Tx pin I can see the response to the AT Command:

~!Ed{@6m[y]^d]=[zr9iP0:/topic/esp-pppos/cmux{"sentFrom":"ESP32 via PPPoS CMUX"}]?~  
+CPSI: LTE,Online,xxx-xx,0x2xxx,xxxxxxxx,xx,EUTRAN-BAND3,xxxx,5,5,-100,-907,-608,16

OK
david-cermak commented 1 year ago

@StarOfSlytherin Have you tried to extend the timeout a bit? the device might reply in 100ms, but the real terminal is shared with network virtual terminal, so might be locked for receving/sending network packets. I think a little longer response in CMUX compared to the plain DATA mode is somewhat expected.

StarOfSlytherin commented 1 year ago

I've tried it with 9000ms too as per the datasheet (Max response time) and it has not worked. It's a bit weird as functions like esp_modem_get_signal_quality or esp_modem_at(dce, "AT+CPMUTEMP", at_response, 100); work fine. AT+CPSI? does not work most of the time. By any chance is it because of the response length?

Edit: Played around with the timing and it works now.