esp_modem: Initial version based on IDF 4.3

This commit is contained in:
David Cermak
2020-11-18 21:15:08 +01:00
parent d42b6670c5
commit 5565a09ed1
20 changed files with 2776 additions and 0 deletions

12
esp_modem/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
set(srcs "src/esp_modem.c"
"src/esp_modem_dce_service"
"src/esp_modem_netif.c"
"src/esp_modem_compat.c"
"src/sim800.c"
"src/sim7600.c"
"src/bg96.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
REQUIRES driver)

9
esp_modem/Kconfig Normal file
View File

@ -0,0 +1,9 @@
menu "ESP-MODEM"
config EXAMPLE_COMPONENT_MODEM_APN
string "Set Access Point Name (APN)"
default "CMNET"
help
Logical name which is used to select the GGSN or the external packet data network.
endmenu

3
esp_modem/component.mk Normal file
View File

@ -0,0 +1,3 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include
COMPONENT_SRCDIRS := src

33
esp_modem/include/bg96.h Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_modem_dce_service.h"
#include "esp_modem.h"
/**
* @brief Create and initialize BG96 object
*
* @param dte Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
modem_dce_t *bg96_init(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,174 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_modem_dce.h"
#include "esp_modem_dte.h"
#include "esp_event.h"
#include "driver/uart.h"
#include "esp_modem_compat.h"
/**
* @brief Declare Event Base for ESP Modem
*
*/
ESP_EVENT_DECLARE_BASE(ESP_MODEM_EVENT);
/**
* @brief ESP Modem Event
*
*/
typedef enum {
ESP_MODEM_EVENT_PPP_START = 0, /*!< ESP Modem Start PPP Session */
ESP_MODEM_EVENT_PPP_STOP = 3, /*!< ESP Modem Stop PPP Session*/
ESP_MODEM_EVENT_UNKNOWN = 4 /*!< ESP Modem Unknown Response */
} esp_modem_event_t;
/**
* @brief ESP Modem DTE Configuration
*
*/
typedef struct {
uart_port_t port_num; /*!< UART port number */
uart_word_length_t data_bits; /*!< Data bits of UART */
uart_stop_bits_t stop_bits; /*!< Stop bits of UART */
uart_parity_t parity; /*!< Parity type */
modem_flow_ctrl_t flow_control; /*!< Flow control type */
uint32_t baud_rate; /*!< Communication baud rate */
int tx_io_num; /*!< TXD Pin Number */
int rx_io_num; /*!< RXD Pin Number */
int rts_io_num; /*!< RTS Pin Number */
int cts_io_num; /*!< CTS Pin Number */
int rx_buffer_size; /*!< UART RX Buffer Size */
int tx_buffer_size; /*!< UART TX Buffer Size */
int pattern_queue_size; /*!< UART Pattern Queue Size */
int event_queue_size; /*!< UART Event Queue Size */
uint32_t event_task_stack_size; /*!< UART Event Task Stack size */
int event_task_priority; /*!< UART Event Task Priority */
int line_buffer_size; /*!< Line buffer size for command mode */
} esp_modem_dte_config_t;
/**
* @brief Type used for reception callback
*
*/
typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *context);
/**
* @brief ESP Modem DTE Default Configuration
*
*/
#define ESP_MODEM_DTE_DEFAULT_CONFIG() \
{ \
.port_num = UART_NUM_1, \
.data_bits = UART_DATA_8_BITS, \
.stop_bits = UART_STOP_BITS_1, \
.parity = UART_PARITY_DISABLE, \
.baud_rate = 115200, \
.flow_control = MODEM_FLOW_CONTROL_NONE,\
.tx_io_num = 25, \
.rx_io_num = 26, \
.rts_io_num = 27, \
.cts_io_num = 23, \
.rx_buffer_size = 1024, \
.tx_buffer_size = 512, \
.pattern_queue_size = 20, \
.event_queue_size = 30, \
.event_task_stack_size = 2048, \
.event_task_priority = 5, \
.line_buffer_size = 512 \
}
/**
* @brief Create and initialize Modem DTE object
*
* @param config configuration of ESP Modem DTE object
* @return modem_dte_t*
* - Modem DTE object
*/
modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config);
/**
* @brief Register event handler for ESP Modem event loop
*
* @param dte modem_dte_t type object
* @param handler event handler to register
* @param handler_args arguments for registered handler
* @return esp_err_t
* - ESP_OK on success
* - ESP_ERR_NO_MEM on allocating memory for the handler failed
* - ESP_ERR_INVALID_ARG on invalid combination of event base and event id
*/
esp_err_t esp_modem_set_event_handler(modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args);
/**
* @brief Unregister event handler for ESP Modem event loop
*
* @param dte modem_dte_t type object
* @param handler event handler to unregister
* @return esp_err_t
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG on invalid combination of event base and event id
*/
esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t handler);
/**
* @brief Setup PPP Session
*
* @param dte Modem DTE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_start_ppp(modem_dte_t *dte);
/**
* @brief Exit PPP Session
*
* @param dte Modem DTE Object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_stop_ppp(modem_dte_t *dte);
/**
* @brief Setup on reception callback
*
* @param dte ESP Modem DTE object
* @param receive_cb Function pointer to the reception callback
* @param receive_cb_ctx Contextual pointer to be passed to the reception callback
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_set_rx_cb(modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx);
/**
* @brief Notify the modem, that ppp netif has closed
*
* @note This API should only be used internally by the modem-netif layer
*
* @param dte ESP Modem DTE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_notify_ppp_netif_closed(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,62 @@
// Copyright 2019 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
#ifdef __cplusplus
extern "C" {
#endif
#include "lwip/ip.h"
/**
* @brief ESP Modem Event backward compatible version
*/
typedef enum {
MODEM_EVENT_PPP_START = 0x100,
MODEM_EVENT_PPP_CONNECT = 0x101,
MODEM_EVENT_PPP_DISCONNECT = 0x102,
MODEM_EVENT_PPP_STOP = 0x103,
MODEM_EVENT_UNKNOWN = 0x104,
} esp_modem_compat_event_t;
/**
* @brief PPPoS Client IP Information backward compatible version
*
*/
typedef struct {
ip4_addr_t ip; /*!< IP Address */
ip4_addr_t netmask; /*!< Net Mask */
ip4_addr_t gw; /*!< Gateway */
ip4_addr_t ns1; /*!< Name Server1 */
ip4_addr_t ns2; /*!< Name Server2 */
} ppp_client_ip_info_t;
/**
* @brief Backward compatible version of esp_modem_set_event_handler()
*/
esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args) __attribute__ ((deprecated));
/**
* @brief Backward compatible version of creating esp-netif(PPP) and attaching to esp_modem_start_ppp()
*/
esp_err_t esp_modem_setup_ppp(modem_dte_t *dte) __attribute__ ((deprecated));
/**
* @brief Backward compatible version of deleting esp-netif and esp_modem_stop_ppp()
*/
esp_err_t esp_modem_exit_ppp(modem_dte_t *dte) __attribute__ ((deprecated));
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,99 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_types.h"
#include "esp_err.h"
#include "esp_modem_dte.h"
typedef struct modem_dce modem_dce_t;
typedef struct modem_dte modem_dte_t;
/**
* @brief Result Code from DCE
*
*/
#define MODEM_RESULT_CODE_SUCCESS "OK" /*!< Acknowledges execution of a command */
#define MODEM_RESULT_CODE_CONNECT "CONNECT" /*!< A connection has been established */
#define MODEM_RESULT_CODE_RING "RING" /*!< Detect an incoming call signal from network */
#define MODEM_RESULT_CODE_NO_CARRIER "NO CARRIER" /*!< Connection termincated or establish a connection failed */
#define MODEM_RESULT_CODE_ERROR "ERROR" /*!< Command not recognized, command line maximum length exceeded, parameter value invalid */
#define MODEM_RESULT_CODE_NO_DIALTONE "NO DIALTONE" /*!< No dial tone detected */
#define MODEM_RESULT_CODE_BUSY "BUSY" /*!< Engaged signal detected */
#define MODEM_RESULT_CODE_NO_ANSWER "NO ANSWER" /*!< Wait for quiet answer */
/**
* @brief Specific Length Constraint
*
*/
#define MODEM_MAX_NAME_LENGTH (32) /*!< Max Module Name Length */
#define MODEM_MAX_OPERATOR_LENGTH (32) /*!< Max Operator Name Length */
#define MODEM_IMEI_LENGTH (15) /*!< IMEI Number Length */
#define MODEM_IMSI_LENGTH (15) /*!< IMSI Number Length */
/**
* @brief Specific Timeout Constraint, Unit: millisecond
*
*/
#define MODEM_COMMAND_TIMEOUT_DEFAULT (500) /*!< Default timeout value for most commands */
#define MODEM_COMMAND_TIMEOUT_OPERATOR (75000) /*!< Timeout value for getting operator status */
#define MODEM_COMMAND_TIMEOUT_MODE_CHANGE (5000) /*!< Timeout value for changing working mode */
#define MODEM_COMMAND_TIMEOUT_HANG_UP (90000) /*!< Timeout value for hang up */
#define MODEM_COMMAND_TIMEOUT_POWEROFF (1000) /*!< Timeout value for power down */
/**
* @brief Working state of DCE
*
*/
typedef enum {
MODEM_STATE_PROCESSING, /*!< In processing */
MODEM_STATE_SUCCESS, /*!< Process successfully */
MODEM_STATE_FAIL /*!< Process failed */
} modem_state_t;
/**
* @brief DCE(Data Communication Equipment)
*
*/
struct modem_dce {
char imei[MODEM_IMEI_LENGTH + 1]; /*!< IMEI number */
char imsi[MODEM_IMSI_LENGTH + 1]; /*!< IMSI number */
char name[MODEM_MAX_NAME_LENGTH]; /*!< Module name */
char oper[MODEM_MAX_OPERATOR_LENGTH]; /*!< Operator name */
modem_state_t state; /*!< Modem working state */
modem_mode_t mode; /*!< Working mode */
modem_dte_t *dte; /*!< DTE which connect to DCE */
esp_err_t (*handle_line)(modem_dce_t *dce, const char *line); /*!< Handle line strategy */
esp_err_t (*sync)(modem_dce_t *dce); /*!< Synchronization */
esp_err_t (*echo_mode)(modem_dce_t *dce, bool on); /*!< Echo command on or off */
esp_err_t (*store_profile)(modem_dce_t *dce); /*!< Store user settings */
esp_err_t (*set_flow_ctrl)(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl); /*!< Flow control on or off */
esp_err_t (*get_signal_quality)(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber); /*!< Get signal quality */
esp_err_t (*get_battery_status)(modem_dce_t *dce, uint32_t *bcs,
uint32_t *bcl, uint32_t *voltage); /*!< Get battery status */
esp_err_t (*define_pdp_context)(modem_dce_t *dce, uint32_t cid,
const char *type, const char *apn); /*!< Set PDP Contex */
esp_err_t (*set_working_mode)(modem_dce_t *dce, modem_mode_t mode); /*!< Set working mode */
esp_err_t (*hang_up)(modem_dce_t *dce); /*!< Hang up */
esp_err_t (*power_down)(modem_dce_t *dce); /*!< Normal power down */
esp_err_t (*deinit)(modem_dce_t *dce); /*!< Deinitialize */
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,131 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_modem_dce.h"
/**
* @brief Indicate that processing current command has done
*
* @param dce Modem DCE object
* @param state Modem state after processing
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static inline esp_err_t esp_modem_process_command_done(modem_dce_t *dce, modem_state_t state)
{
dce->state = state;
return dce->dte->process_cmd_done(dce->dte);
}
/**
* @brief Strip the tailed "\r\n"
*
* @param str string to strip
* @param len length of string
*/
static inline void strip_cr_lf_tail(char *str, uint32_t len)
{
if (str[len - 2] == '\r') {
str[len - 2] = '\0';
} else if (str[len - 1] == '\r') {
str[len - 1] = '\0';
}
}
/**
* @brief Default handler for response
* Some responses for command are simple, commonly will return OK when succeed of ERROR when failed
*
* @param dce Modem DCE object
* @param line line string
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_handle_response_default(modem_dce_t *dce, const char *line);
/**
* @brief Syncronization
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_sync(modem_dce_t *dce);
/**
* @brief Enable or not echo mode of DCE
*
* @param dce Modem DCE object
* @param on true to enable echo mode, false to disable echo mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_echo(modem_dce_t *dce, bool on);
/**
* @brief Store current parameter setting in the user profile
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_store_profile(modem_dce_t *dce);
/**
* @brief Set flow control mode of DCE in data mode
*
* @param dce Modem DCE object
* @param flow_ctrl flow control mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_set_flow_ctrl(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl);
/**
* @brief Define PDP context
*
* @param dce Modem DCE object
* @param cid PDP context identifier
* @param type Protocol type
* @param apn Access point name
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn);
/**
* @brief Hang up
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_hang_up(modem_dce_t *dce);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,66 @@
// Copyright 2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_types.h"
#include "esp_err.h"
#include "esp_event.h"
typedef struct modem_dte modem_dte_t;
typedef struct modem_dce modem_dce_t;
/**
* @brief Working mode of Modem
*
*/
typedef enum {
MODEM_COMMAND_MODE = 0, /*!< Command Mode */
MODEM_PPP_MODE, /*!< PPP Mode */
MODEM_TRANSITION_MODE /*!< Transition Mode betwen data and command mode indicating that
the modem is not yet ready for sending commands nor data */
} modem_mode_t;
/**
* @brief Modem flow control type
*
*/
typedef enum {
MODEM_FLOW_CONTROL_NONE = 0,
MODEM_FLOW_CONTROL_SW,
MODEM_FLOW_CONTROL_HW
} modem_flow_ctrl_t;
/**
* @brief DTE(Data Terminal Equipment)
*
*/
struct modem_dte {
modem_flow_ctrl_t flow_ctrl; /*!< Flow control of DTE */
modem_dce_t *dce; /*!< DCE which connected to the DTE */
esp_err_t (*send_cmd)(modem_dte_t *dte, const char *command, uint32_t timeout); /*!< Send command to DCE */
int (*send_data)(modem_dte_t *dte, const char *data, uint32_t length); /*!< Send data to DCE */
esp_err_t (*send_wait)(modem_dte_t *dte, const char *data, uint32_t length,
const char *prompt, uint32_t timeout); /*!< Wait for specific prompt */
esp_err_t (*change_mode)(modem_dte_t *dte, modem_mode_t new_mode); /*!< Changing working mode */
esp_err_t (*process_cmd_done)(modem_dte_t *dte); /*!< Callback when DCE process command done */
esp_err_t (*deinit)(modem_dte_t *dte); /*!< Deinitialize */
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,53 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Creates handle to esp_modem used as an esp-netif driver
*
* @param dte ESP Modem DTE object
*
* @return opaque pointer to esp-modem IO driver used to attach to esp-netif
*/
void *esp_modem_netif_setup(modem_dte_t *dte);
/**
* @brief Destroys the esp-netif driver handle
*
* @param h pointer to the esp-netif adapter for esp-modem
*/
void esp_modem_netif_teardown(void *h);
/**
* @brief Clears default handlers for esp-modem lifecycle
*
* @param h pointer to the esp-netif adapter for esp-modem
*/
esp_err_t esp_modem_netif_clear_default_handlers(void *h);
/**
* @brief Setups default handlers for esp-modem lifecycle
*
* @param h pointer to the esp-netif adapter for esp-modem
* @param esp_netif pointer corresponding esp-netif instance
*/
esp_err_t esp_modem_netif_set_default_handlers(void *h, esp_netif_t * esp_netif);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,33 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_modem_dce_service.h"
#include "esp_modem.h"
/**
* @brief Create and initialize SIM7600 object
*
* @param dte Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
modem_dce_t *sim7600_init(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,33 @@
// Copyright 2015-2018 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
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_modem_dce_service.h"
#include "esp_modem.h"
/**
* @brief Create and initialize SIM800 object
*
* @param dte Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
modem_dce_t *sim800_init(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,37 @@
// Copyright 2015-2018 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
/**
* @brief Macro defined for error checking
*
*/
#define DCE_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
/**
* @brief BG96 Modem
*
*/
typedef struct {
void *priv_resource; /*!< Private resource */
modem_dce_t parent; /*!< DCE parent class */
} bg96_modem_dce_t;

472
esp_modem/src/bg96.c Normal file
View File

@ -0,0 +1,472 @@
// Copyright 2015-2018 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.
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "bg96.h"
#include "bg96_private.h"
#define MODEM_RESULT_CODE_POWERDOWN "POWERED DOWN"
static const char *DCE_TAG = "bg96";
/**
* @brief Handle response from AT+CSQ
*/
static esp_err_t bg96_handle_csq(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CSQ", strlen("+CSQ"))) {
/* store value of rssi and ber */
uint32_t **csq = bg96_dce->priv_resource;
/* +CSQ: <rssi>,<ber> */
sscanf(line, "%*s%d,%d", csq[0], csq[1]);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t bg96_handle_cbc(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
/* store value of bcs, bcl, voltage */
uint32_t **cbc = bg96_dce->priv_resource;
/* +CBC: <bcs>,<bcl>,<voltage> */
sscanf(line, "%*s%d,%d,%d", cbc[0], cbc[1], cbc[2]);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from +++
*/
static esp_err_t bg96_handle_exit_data_mode(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Handle response from ATD*99#
*/
static esp_err_t bg96_handle_atd_ppp(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_CONNECT)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Handle response from AT+CGMM
*/
static esp_err_t bg96_handle_cgmm(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->name, MODEM_MAX_NAME_LENGTH, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->name, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+CGSN
*/
static esp_err_t bg96_handle_cgsn(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->imei, MODEM_IMEI_LENGTH + 1, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->imei, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+CIMI
*/
static esp_err_t bg96_handle_cimi(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->imsi, MODEM_IMSI_LENGTH + 1, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->imsi, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+COPS?
*/
static esp_err_t bg96_handle_cops(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+COPS", strlen("+COPS"))) {
/* there might be some random spaces in operator's name, we can not use sscanf to parse the result */
/* strtok will break the string, we need to create a copy */
size_t len = strlen(line);
char *line_copy = malloc(len + 1);
strcpy(line_copy, line);
/* +COPS: <mode>[, <format>[, <oper>]] */
char *str_ptr = NULL;
char *p[3];
uint8_t i = 0;
/* strtok will broke string by replacing delimiter with '\0' */
p[i] = strtok_r(line_copy, ",", &str_ptr);
while (p[i]) {
p[++i] = strtok_r(NULL, ",", &str_ptr);
}
if (i >= 3) {
int len = snprintf(dce->oper, MODEM_MAX_OPERATOR_LENGTH, "%s", p[2]);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->oper, len);
err = ESP_OK;
}
}
free(line_copy);
}
return err;
}
/**
* @brief Handle response from AT+QPOWD=1
*/
static esp_err_t bg96_handle_power_down(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = ESP_OK;
} else if (strstr(line, MODEM_RESULT_CODE_POWERDOWN)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
}
return err;
}
/**
* @brief Get signal quality
*
* @param dce Modem DCE object
* @param rssi received signal strength indication
* @param ber bit error ratio
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_signal_quality(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber)
{
modem_dte_t *dte = dce->dte;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
uint32_t *resource[2] = {rssi, ber};
bg96_dce->priv_resource = resource;
dce->handle_line = bg96_handle_csq;
DCE_CHECK(dte->send_cmd(dte, "AT+CSQ\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire signal quality failed", err);
ESP_LOGD(DCE_TAG, "inquire signal quality ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get battery status
*
* @param dce Modem DCE object
* @param bcs Battery charge status
* @param bcl Battery connection level
* @param voltage Battery voltage
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
{
modem_dte_t *dte = dce->dte;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
uint32_t *resource[3] = {bcs, bcl, voltage};
bg96_dce->priv_resource = resource;
dce->handle_line = bg96_handle_cbc;
DCE_CHECK(dte->send_cmd(dte, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire battery status failed", err);
ESP_LOGD(DCE_TAG, "inquire battery status ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Set Working Mode
*
* @param dce Modem DCE object
* @param mode woking mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
{
modem_dte_t *dte = dce->dte;
switch (mode) {
case MODEM_COMMAND_MODE:
vTaskDelay(pdMS_TO_TICKS(1000)); // spec: 1s delay for the modem to recognize the escape sequence
dce->handle_line = bg96_handle_exit_data_mode;
if (dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) != ESP_OK) {
// "+++" Could fail if we are already in the command mode.
// in that case we ignore the timout and re-sync the modem
ESP_LOGI(DCE_TAG, "Sending \"+++\" command failed");
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, "AT\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "sync failed", err);
} else {
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter command mode failed", err);
}
ESP_LOGD(DCE_TAG, "enter command mode ok");
dce->mode = MODEM_COMMAND_MODE;
break;
case MODEM_PPP_MODE:
dce->handle_line = bg96_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATD*99***1#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
if (dce->state != MODEM_STATE_SUCCESS) {
// Initiate PPP mode could fail, if we've already "dialed" the data call before.
// in that case we retry with "ATO" to just resume the data mode
ESP_LOGD(DCE_TAG, "enter ppp mode failed, retry with ATO");
dce->handle_line = bg96_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATO\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter ppp mode failed", err);
}
ESP_LOGD(DCE_TAG, "enter ppp mode ok");
dce->mode = MODEM_PPP_MODE;
break;
default:
ESP_LOGW(DCE_TAG, "unsupported working mode: %d", mode);
goto err;
break;
}
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Power down
*
* @param bg96_dce bg96 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_power_down(modem_dce_t *dce)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = bg96_handle_power_down;
DCE_CHECK(dte->send_cmd(dte, "AT+QPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "power down failed", err);
ESP_LOGD(DCE_TAG, "power down ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module name
*
* @param bg96_dce bg96 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_module_name(bg96_modem_dce_t *bg96_dce)
{
modem_dte_t *dte = bg96_dce->parent.dte;
bg96_dce->parent.handle_line = bg96_handle_cgmm;
DCE_CHECK(dte->send_cmd(dte, "AT+CGMM\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get module name failed", err);
ESP_LOGD(DCE_TAG, "get module name ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module IMEI number
*
* @param bg96_dce bg96 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_imei_number(bg96_modem_dce_t *bg96_dce)
{
modem_dte_t *dte = bg96_dce->parent.dte;
bg96_dce->parent.handle_line = bg96_handle_cgsn;
DCE_CHECK(dte->send_cmd(dte, "AT+CGSN\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get imei number failed", err);
ESP_LOGD(DCE_TAG, "get imei number ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module IMSI number
*
* @param bg96_dce bg96 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_imsi_number(bg96_modem_dce_t *bg96_dce)
{
modem_dte_t *dte = bg96_dce->parent.dte;
bg96_dce->parent.handle_line = bg96_handle_cimi;
DCE_CHECK(dte->send_cmd(dte, "AT+CIMI\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get imsi number failed", err);
ESP_LOGD(DCE_TAG, "get imsi number ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get Operator's name
*
* @param bg96_dce bg96 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t bg96_get_operator_name(bg96_modem_dce_t *bg96_dce)
{
modem_dte_t *dte = bg96_dce->parent.dte;
bg96_dce->parent.handle_line = bg96_handle_cops;
DCE_CHECK(dte->send_cmd(dte, "AT+COPS?\r", MODEM_COMMAND_TIMEOUT_OPERATOR) == ESP_OK, "send command failed", err);
DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get network operator failed", err);
ESP_LOGD(DCE_TAG, "get network operator ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Deinitialize BG96 object
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on fail
*/
static esp_err_t bg96_deinit(modem_dce_t *dce)
{
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
if (dce->dte) {
dce->dte->dce = NULL;
}
free(bg96_dce);
return ESP_OK;
}
modem_dce_t *bg96_init(modem_dte_t *dte)
{
DCE_CHECK(dte, "DCE should bind with a DTE", err);
/* malloc memory for bg96_dce object */
bg96_modem_dce_t *bg96_dce = calloc(1, sizeof(bg96_modem_dce_t));
DCE_CHECK(bg96_dce, "calloc bg96_dce failed", err);
/* Bind DTE with DCE */
bg96_dce->parent.dte = dte;
dte->dce = &(bg96_dce->parent);
/* Bind methods */
bg96_dce->parent.handle_line = NULL;
bg96_dce->parent.sync = esp_modem_dce_sync;
bg96_dce->parent.echo_mode = esp_modem_dce_echo;
bg96_dce->parent.store_profile = esp_modem_dce_store_profile;
bg96_dce->parent.set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
bg96_dce->parent.define_pdp_context = esp_modem_dce_define_pdp_context;
bg96_dce->parent.hang_up = esp_modem_dce_hang_up;
bg96_dce->parent.get_signal_quality = bg96_get_signal_quality;
bg96_dce->parent.get_battery_status = bg96_get_battery_status;
bg96_dce->parent.set_working_mode = bg96_set_working_mode;
bg96_dce->parent.power_down = bg96_power_down;
bg96_dce->parent.deinit = bg96_deinit;
/* Sync between DTE and DCE */
DCE_CHECK(esp_modem_dce_sync(&(bg96_dce->parent)) == ESP_OK, "sync failed", err_io);
/* Close echo */
DCE_CHECK(esp_modem_dce_echo(&(bg96_dce->parent), false) == ESP_OK, "close echo mode failed", err_io);
/* Get Module name */
DCE_CHECK(bg96_get_module_name(bg96_dce) == ESP_OK, "get module name failed", err_io);
/* Get IMEI number */
DCE_CHECK(bg96_get_imei_number(bg96_dce) == ESP_OK, "get imei failed", err_io);
/* Get IMSI number */
DCE_CHECK(bg96_get_imsi_number(bg96_dce) == ESP_OK, "get imsi failed", err_io);
/* Get operator name */
DCE_CHECK(bg96_get_operator_name(bg96_dce) == ESP_OK, "get operator name failed", err_io);
return &(bg96_dce->parent);
err_io:
free(bg96_dce);
err:
return NULL;
}

566
esp_modem/src/esp_modem.c Normal file
View File

@ -0,0 +1,566 @@
// Copyright 2015-2018 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.
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_modem.h"
#include "esp_log.h"
#include "sdkconfig.h"
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
#define MIN_PATTERN_INTERVAL (9)
#define MIN_POST_IDLE (0)
#define MIN_PRE_IDLE (0)
/**
* @brief Macro defined for error checking
*
*/
static const char *MODEM_TAG = "esp-modem";
#define MODEM_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(MODEM_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
ESP_EVENT_DEFINE_BASE(ESP_MODEM_EVENT);
/**
* @brief ESP32 Modem DTE
*
*/
typedef struct {
uart_port_t uart_port; /*!< UART port */
uint8_t *buffer; /*!< Internal buffer to store response lines/data from DCE */
QueueHandle_t event_queue; /*!< UART event queue handle */
esp_event_loop_handle_t event_loop_hdl; /*!< Event loop handle */
TaskHandle_t uart_event_task_hdl; /*!< UART event task handle */
SemaphoreHandle_t process_sem; /*!< Semaphore used for indicating processing status */
SemaphoreHandle_t exit_sem; /*!< Semaphore used for indicating PPP mode has stopped */
modem_dte_t parent; /*!< DTE interface that should extend */
esp_modem_on_receive receive_cb; /*!< ptr to data reception */
void *receive_cb_ctx; /*!< ptr to rx fn context data */
int line_buffer_size; /*!< line buffer size in commnad mode */
int pattern_queue_size; /*!< UART pattern queue size */
} esp_modem_dte_t;
/**
* @brief Returns true if the supplied string contains only CR or LF
*
* @param str string to check
* @param len length of string
*/
static inline bool is_only_cr_lf(const char *str, uint32_t len)
{
for (int i=0; i<len; ++i) {
if (str[i] != '\r' && str[i] != '\n') {
return false;
}
}
return true;
}
esp_err_t esp_modem_set_rx_cb(modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
esp_dte->receive_cb_ctx = receive_cb_ctx;
esp_dte->receive_cb = receive_cb;
return ESP_OK;
}
/**
* @brief Handle one line in DTE
*
* @param esp_dte ESP modem DTE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t esp_dte_handle_line(esp_modem_dte_t *esp_dte)
{
modem_dce_t *dce = esp_dte->parent.dce;
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
const char *line = (const char *)(esp_dte->buffer);
size_t len = strlen(line);
/* Skip pure "\r\n" lines */
if (len > 2 && !is_only_cr_lf(line, len)) {
MODEM_CHECK(dce->handle_line, "no handler for line", err_handle);
MODEM_CHECK(dce->handle_line(dce, line) == ESP_OK, "handle line failed", err_handle);
}
return ESP_OK;
err_handle:
/* Send ESP_MODEM_EVENT_UNKNOWN signal to event loop */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_MODEM_EVENT_UNKNOWN,
(void *)line, strlen(line) + 1, pdMS_TO_TICKS(100));
err:
return ESP_FAIL;
}
/**
* @brief Handle when a pattern has been detected by UART
*
* @param esp_dte ESP32 Modem DTE object
*/
static void esp_handle_uart_pattern(esp_modem_dte_t *esp_dte)
{
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
int read_len = 0;
if (pos != -1) {
if (pos < esp_dte->line_buffer_size - 1) {
/* read one line(include '\n') */
read_len = pos + 1;
} else {
ESP_LOGW(MODEM_TAG, "ESP Modem Line buffer too small");
read_len = esp_dte->line_buffer_size - 1;
}
read_len = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, read_len, pdMS_TO_TICKS(100));
if (read_len) {
/* make sure the line is a standard string */
esp_dte->buffer[read_len] = '\0';
/* Send new line to handle */
esp_dte_handle_line(esp_dte);
} else {
ESP_LOGE(MODEM_TAG, "uart read bytes failed");
}
} else {
size_t length = 0;
uart_get_buffered_data_len(esp_dte->uart_port, &length);
ESP_LOGW(MODEM_TAG, "Pattern not found in the pattern queue, uart data length = %d", length);
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
uart_flush(esp_dte->uart_port);
}
}
/**
* @brief Handle when new data received by UART
*
* @param esp_dte ESP32 Modem DTE object
*/
static void esp_handle_uart_data(esp_modem_dte_t *esp_dte)
{
size_t length = 0;
uart_get_buffered_data_len(esp_dte->uart_port, &length);
if (esp_dte->parent.dce->mode != MODEM_PPP_MODE) {
// Check if matches the pattern to process the data as pattern
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
if (pos > -1) {
esp_handle_uart_pattern(esp_dte);
return;
}
// Read the data and process it using `handle_line` logic
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
esp_dte->buffer[length] = '\0';
if (esp_dte->parent.dce->handle_line) {
/* Send new line to handle if handler registered */
esp_dte_handle_line(esp_dte);
}
return;
}
length = MIN(esp_dte->line_buffer_size, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
/* pass the input data to configured callback */
if (length) {
esp_dte->receive_cb(esp_dte->buffer, length, esp_dte->receive_cb_ctx);
}
}
/**
* @brief UART Event Task Entry
*
* @param param task parameter
*/
static void uart_event_task_entry(void *param)
{
esp_modem_dte_t *esp_dte = (esp_modem_dte_t *)param;
uart_event_t event;
while (1) {
if (xQueueReceive(esp_dte->event_queue, &event, pdMS_TO_TICKS(100))) {
switch (event.type) {
case UART_DATA:
esp_handle_uart_data(esp_dte);
break;
case UART_FIFO_OVF:
ESP_LOGW(MODEM_TAG, "HW FIFO Overflow");
uart_flush_input(esp_dte->uart_port);
xQueueReset(esp_dte->event_queue);
break;
case UART_BUFFER_FULL:
ESP_LOGW(MODEM_TAG, "Ring Buffer Full");
uart_flush_input(esp_dte->uart_port);
xQueueReset(esp_dte->event_queue);
break;
case UART_BREAK:
ESP_LOGW(MODEM_TAG, "Rx Break");
break;
case UART_PARITY_ERR:
ESP_LOGE(MODEM_TAG, "Parity Error");
break;
case UART_FRAME_ERR:
ESP_LOGE(MODEM_TAG, "Frame Error");
break;
case UART_PATTERN_DET:
esp_handle_uart_pattern(esp_dte);
break;
default:
ESP_LOGW(MODEM_TAG, "unknown uart event type: %d", event.type);
break;
}
}
/* Drive the event loop */
esp_event_loop_run(esp_dte->event_loop_hdl, pdMS_TO_TICKS(50));
}
vTaskDelete(NULL);
}
/**
* @brief Send command to DCE
*
* @param dte Modem DTE object
* @param command command string
* @param timeout timeout value, unit: ms
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t esp_modem_dte_send_cmd(modem_dte_t *dte, const char *command, uint32_t timeout)
{
esp_err_t ret = ESP_FAIL;
modem_dce_t *dce = dte->dce;
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
MODEM_CHECK(command, "command is NULL", err);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
/* Calculate timeout clock tick */
/* Reset runtime information */
dce->state = MODEM_STATE_PROCESSING;
/* Send command via UART */
uart_write_bytes(esp_dte->uart_port, command, strlen(command));
/* Check timeout */
MODEM_CHECK(xSemaphoreTake(esp_dte->process_sem, pdMS_TO_TICKS(timeout)) == pdTRUE, "process command timeout", err);
ret = ESP_OK;
err:
dce->handle_line = NULL;
return ret;
}
/**
* @brief Send data to DCE
*
* @param dte Modem DTE object
* @param data data buffer
* @param length length of data to send
* @return int actual length of data that has been send out
*/
static int esp_modem_dte_send_data(modem_dte_t *dte, const char *data, uint32_t length)
{
MODEM_CHECK(data, "data is NULL", err);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
if (esp_dte->parent.dce->mode == MODEM_TRANSITION_MODE) {
ESP_LOGD(MODEM_TAG, "Not sending data in transition mode");
return -1;
}
return uart_write_bytes(esp_dte->uart_port, data, length);
err:
return -1;
}
/**
* @brief Send data and wait for prompt from DCE
*
* @param dte Modem DTE object
* @param data data buffer
* @param length length of data to send
* @param prompt pointer of specific prompt
* @param timeout timeout value (unit: ms)
* @return esp_err_t
* ESP_OK on success
* ESP_FAIL on error
*/
static esp_err_t esp_modem_dte_send_wait(modem_dte_t *dte, const char *data, uint32_t length,
const char *prompt, uint32_t timeout)
{
MODEM_CHECK(data, "data is NULL", err_param);
MODEM_CHECK(prompt, "prompt is NULL", err_param);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
// We'd better disable pattern detection here for a moment in case prompt string contains the pattern character
uart_disable_pattern_det_intr(esp_dte->uart_port);
// uart_disable_rx_intr(esp_dte->uart_port);
MODEM_CHECK(uart_write_bytes(esp_dte->uart_port, data, length) >= 0, "uart write bytes failed", err_write);
uint32_t len = strlen(prompt);
uint8_t *buffer = calloc(len + 1, sizeof(uint8_t));
int res = uart_read_bytes(esp_dte->uart_port, buffer, len, pdMS_TO_TICKS(timeout));
MODEM_CHECK(res >= len, "wait prompt [%s] timeout", err, prompt);
MODEM_CHECK(!strncmp(prompt, (const char *)buffer, len), "get wrong prompt: %s", err, buffer);
free(buffer);
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
return ESP_OK;
err:
free(buffer);
err_write:
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
err_param:
return ESP_FAIL;
}
/**
* @brief Change Modem's working mode
*
* @param dte Modem DTE object
* @param new_mode new working mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t esp_modem_dte_change_mode(modem_dte_t *dte, modem_mode_t new_mode)
{
modem_dce_t *dce = dte->dce;
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
modem_mode_t current_mode = dce->mode;
MODEM_CHECK(current_mode != new_mode, "already in mode: %d", err, new_mode);
dce->mode = MODEM_TRANSITION_MODE; // mode switching will be finished in set_working_mode() on success
// (or restored on failure)
switch (new_mode) {
case MODEM_PPP_MODE:
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
uart_disable_pattern_det_intr(esp_dte->uart_port);
uart_enable_rx_intr(esp_dte->uart_port);
break;
case MODEM_COMMAND_MODE:
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
uart_disable_rx_intr(esp_dte->uart_port);
uart_flush(esp_dte->uart_port);
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
uart_pattern_queue_reset(esp_dte->uart_port, esp_dte->pattern_queue_size);
break;
default:
break;
}
return ESP_OK;
err_restore_mode:
dce->mode = current_mode;
err:
return ESP_FAIL;
}
static esp_err_t esp_modem_dte_process_cmd_done(modem_dte_t *dte)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
return xSemaphoreGive(esp_dte->process_sem) == pdTRUE ? ESP_OK : ESP_FAIL;
}
/**
* @brief Deinitialize a Modem DTE object
*
* @param dte Modem DTE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t esp_modem_dte_deinit(modem_dte_t *dte)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
/* Delete UART event task */
vTaskDelete(esp_dte->uart_event_task_hdl);
/* Delete semaphores */
vSemaphoreDelete(esp_dte->process_sem);
vSemaphoreDelete(esp_dte->exit_sem);
/* Delete event loop */
esp_event_loop_delete(esp_dte->event_loop_hdl);
/* Uninstall UART Driver */
uart_driver_delete(esp_dte->uart_port);
/* Free memory */
free(esp_dte->buffer);
if (dte->dce) {
dte->dce->dte = NULL;
}
free(esp_dte);
return ESP_OK;
}
modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
{
esp_err_t res;
/* malloc memory for esp_dte object */
esp_modem_dte_t *esp_dte = calloc(1, sizeof(esp_modem_dte_t));
MODEM_CHECK(esp_dte, "calloc esp_dte failed", err_dte_mem);
/* malloc memory to storing lines from modem dce */
esp_dte->line_buffer_size = config->line_buffer_size;
esp_dte->buffer = calloc(1, config->line_buffer_size);
MODEM_CHECK(esp_dte->buffer, "calloc line memory failed", err_line_mem);
/* Set attributes */
esp_dte->uart_port = config->port_num;
esp_dte->parent.flow_ctrl = config->flow_control;
/* Bind methods */
esp_dte->parent.send_cmd = esp_modem_dte_send_cmd;
esp_dte->parent.send_data = esp_modem_dte_send_data;
esp_dte->parent.send_wait = esp_modem_dte_send_wait;
esp_dte->parent.change_mode = esp_modem_dte_change_mode;
esp_dte->parent.process_cmd_done = esp_modem_dte_process_cmd_done;
esp_dte->parent.deinit = esp_modem_dte_deinit;
/* Config UART */
uart_config_t uart_config = {
.baud_rate = config->baud_rate,
.data_bits = config->data_bits,
.parity = config->parity,
.stop_bits = config->stop_bits,
.source_clk = UART_SCLK_REF_TICK,
.flow_ctrl = (config->flow_control == MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS : UART_HW_FLOWCTRL_DISABLE
};
MODEM_CHECK(uart_param_config(esp_dte->uart_port, &uart_config) == ESP_OK, "config uart parameter failed", err_uart_config);
if (config->flow_control == MODEM_FLOW_CONTROL_HW) {
res = uart_set_pin(esp_dte->uart_port, config->tx_io_num, config->rx_io_num,
config->rts_io_num, config->cts_io_num);
} else {
res = uart_set_pin(esp_dte->uart_port, config->tx_io_num, config->rx_io_num,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
MODEM_CHECK(res == ESP_OK, "config uart gpio failed", err_uart_config);
/* Set flow control threshold */
if (config->flow_control == MODEM_FLOW_CONTROL_HW) {
res = uart_set_hw_flow_ctrl(esp_dte->uart_port, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
} else if (config->flow_control == MODEM_FLOW_CONTROL_SW) {
res = uart_set_sw_flow_ctrl(esp_dte->uart_port, true, 8, UART_FIFO_LEN - 8);
}
MODEM_CHECK(res == ESP_OK, "config uart flow control failed", err_uart_config);
/* Install UART driver and get event queue used inside driver */
res = uart_driver_install(esp_dte->uart_port, config->rx_buffer_size, config->tx_buffer_size,
config->event_queue_size, &(esp_dte->event_queue), 0);
MODEM_CHECK(res == ESP_OK, "install uart driver failed", err_uart_config);
res = uart_set_rx_timeout(esp_dte->uart_port, 1);
MODEM_CHECK(res == ESP_OK, "set rx timeout failed", err_uart_config);
/* Set pattern interrupt, used to detect the end of a line. */
res = uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
/* Set pattern queue size */
esp_dte->pattern_queue_size = config->pattern_queue_size;
res |= uart_pattern_queue_reset(esp_dte->uart_port, config->pattern_queue_size);
/* Starting in command mode -> explicitly disable RX interrupt */
uart_disable_rx_intr(esp_dte->uart_port);
MODEM_CHECK(res == ESP_OK, "config uart pattern failed", err_uart_pattern);
/* Create Event loop */
esp_event_loop_args_t loop_args = {
.queue_size = ESP_MODEM_EVENT_QUEUE_SIZE,
.task_name = NULL
};
MODEM_CHECK(esp_event_loop_create(&loop_args, &esp_dte->event_loop_hdl) == ESP_OK, "create event loop failed", err_eloop);
/* Create semaphore */
esp_dte->process_sem = xSemaphoreCreateBinary();
MODEM_CHECK(esp_dte->process_sem, "create process semaphore failed", err_sem1);
esp_dte->exit_sem = xSemaphoreCreateBinary();
MODEM_CHECK(esp_dte->exit_sem, "create exit semaphore failed", err_sem);
/* Create UART Event task */
BaseType_t ret = xTaskCreate(uart_event_task_entry, //Task Entry
"uart_event", //Task Name
config->event_task_stack_size, //Task Stack Size(Bytes)
esp_dte, //Task Parameter
config->event_task_priority, //Task Priority
& (esp_dte->uart_event_task_hdl) //Task Handler
);
MODEM_CHECK(ret == pdTRUE, "create uart event task failed", err_tsk_create);
return &(esp_dte->parent);
/* Error handling */
err_tsk_create:
vSemaphoreDelete(esp_dte->exit_sem);
err_sem:
vSemaphoreDelete(esp_dte->process_sem);
err_sem1:
esp_event_loop_delete(esp_dte->event_loop_hdl);
err_eloop:
uart_disable_pattern_det_intr(esp_dte->uart_port);
err_uart_pattern:
uart_driver_delete(esp_dte->uart_port);
err_uart_config:
free(esp_dte->buffer);
err_line_mem:
free(esp_dte);
err_dte_mem:
return NULL;
}
esp_err_t esp_modem_set_event_handler(modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
return esp_event_handler_register_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, event_id, handler, handler_args);
}
esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t handler)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
return esp_event_handler_unregister_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_EVENT_ANY_ID, handler);
}
esp_err_t esp_modem_start_ppp(modem_dte_t *dte)
{
modem_dce_t *dce = dte->dce;
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
/* Set PDP Context */
MODEM_CHECK(dce->define_pdp_context(dce, 1, "IP", CONFIG_EXAMPLE_COMPONENT_MODEM_APN) == ESP_OK, "set MODEM APN failed", err);
/* Enter PPP mode */
MODEM_CHECK(dte->change_mode(dte, MODEM_PPP_MODE) == ESP_OK, "enter ppp mode failed", err);
/* post PPP mode started event */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_MODEM_EVENT_PPP_START, NULL, 0, 0);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_stop_ppp(modem_dte_t *dte)
{
modem_dce_t *dce = dte->dce;
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
/* Enter command mode */
MODEM_CHECK(dte->change_mode(dte, MODEM_COMMAND_MODE) == ESP_OK, "enter command mode failed", err);
/* post PPP mode stopped event */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_MODEM_EVENT_PPP_STOP, NULL, 0, 0);
/* Hang up */
MODEM_CHECK(dce->hang_up(dce) == ESP_OK, "hang up failed", err);
/* wait for the PPP mode to exit gracefully */
if (xSemaphoreTake(esp_dte->exit_sem, pdMS_TO_TICKS(20000)) != pdTRUE) {
ESP_LOGW(MODEM_TAG, "Failed to exit the PPP mode gracefully");
}
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_notify_ppp_netif_closed(modem_dte_t *dte)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
return xSemaphoreGive(esp_dte->exit_sem) == pdTRUE ? ESP_OK : ESP_FAIL;
}

View File

@ -0,0 +1,104 @@
// Copyright 2019 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.
#include <string.h>
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_log.h"
static const char *TAG = "esp-modem-compat";
static void on_modem_compat_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
int32_t compat_event_id = MODEM_EVENT_UNKNOWN;
switch (event_id) {
case ESP_MODEM_EVENT_PPP_START:
compat_event_id = MODEM_EVENT_PPP_START;
break;
case ESP_MODEM_EVENT_PPP_STOP:
compat_event_id = MODEM_EVENT_PPP_STOP;
break;
default:
break;
}
esp_event_post(ESP_MODEM_EVENT, compat_event_id, NULL, 0, 0);
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ppp_client_ip_info_t ipinfo = {0};
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
esp_netif_t *netif = event->esp_netif;
ipinfo.ip.addr = event->ip_info.ip.addr;
ipinfo.gw.addr = event->ip_info.gw.addr;
ipinfo.netmask.addr = event->ip_info.netmask.addr;
esp_netif_get_dns_info(netif, 0, &dns_info);
ipinfo.ns1.addr = dns_info.ip.u_addr.ip4.addr;
ipinfo.ns2.addr = dns_info.ip.u_addr.ip4.addr;
esp_event_post(ESP_MODEM_EVENT, MODEM_EVENT_PPP_CONNECT, &ipinfo, sizeof(ipinfo), 0);
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
esp_event_post(ESP_MODEM_EVENT, MODEM_EVENT_PPP_DISCONNECT, NULL, 0, 0);
}
}
esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args)
{
// event loop has to be created when using this API -- create and ignore failure if already created
esp_event_loop_create_default();
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_START, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_CONNECT, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_DISCONNECT, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_STOP, handler, handler_args));
return esp_modem_set_event_handler(dte, on_modem_compat_handler, ESP_EVENT_ANY_ID, handler_args);
}
esp_err_t esp_modem_setup_ppp(modem_dte_t *dte)
{
#if CONFIG_LWIP_PPP_PAP_SUPPORT && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#elif defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
#error "Unsupported AUTH Negotiation while AUTH_USERNAME and PASSWORD defined"
#endif
// Init netif object
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&cfg);
assert(esp_netif);
// event loop has to be created when using this API -- create and ignore failure if already created
esp_event_loop_create_default();
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
#if defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
void *modem_netif_adapter = esp_modem_netif_setup(dte);
esp_modem_netif_set_default_handlers(modem_netif_adapter, esp_netif);
/* attach the modem to the network interface */
return esp_netif_attach(esp_netif, modem_netif_adapter);
}
esp_err_t esp_modem_exit_ppp(modem_dte_t *dte)
{
// Note: A minor memory leak is expected when using esp-modem-compat
return esp_modem_stop_ppp(dte);
}

View File

@ -0,0 +1,126 @@
// Copyright 2015-2018 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.
#include <string.h>
#include "esp_log.h"
#include "esp_modem_dce_service.h"
/**
* @brief Macro defined for error checking
*
*/
static const char *DCE_TAG = "dce_service";
#define DCE_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
esp_err_t esp_modem_dce_handle_response_default(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
esp_err_t esp_modem_dce_sync(modem_dce_t *dce)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, "AT\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "sync failed", err);
ESP_LOGD(DCE_TAG, "sync ok");
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_echo(modem_dce_t *dce, bool on)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = esp_modem_dce_handle_response_default;
if (on) {
DCE_CHECK(dte->send_cmd(dte, "ATE1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enable echo failed", err);
ESP_LOGD(DCE_TAG, "enable echo ok");
} else {
DCE_CHECK(dte->send_cmd(dte, "ATE0\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "disable echo failed", err);
ESP_LOGD(DCE_TAG, "disable echo ok");
}
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_store_profile(modem_dce_t *dce)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, "AT&W\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "save settings failed", err);
ESP_LOGD(DCE_TAG, "save settings ok");
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_set_flow_ctrl(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl)
{
modem_dte_t *dte = dce->dte;
char command[16];
int len = snprintf(command, sizeof(command), "AT+IFC=%d,%d\r", dte->flow_ctrl, flow_ctrl);
DCE_CHECK(len < sizeof(command), "command too long: %s", err, command);
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "set flow control failed", err);
ESP_LOGD(DCE_TAG, "set flow control ok");
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn)
{
modem_dte_t *dte = dce->dte;
char command[64];
int len = snprintf(command, sizeof(command), "AT+CGDCONT=%d,\"%s\",\"%s\"\r", cid, type, apn);
DCE_CHECK(len < sizeof(command), "command too long: %s", err, command);
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "define pdp context failed", err);
ESP_LOGD(DCE_TAG, "define pdp context ok");
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_hang_up(modem_dce_t *dce)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, "ATH\r", MODEM_COMMAND_TIMEOUT_HANG_UP) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "hang up failed", err);
ESP_LOGD(DCE_TAG, "hang up ok");
return ESP_OK;
err:
return ESP_FAIL;
}

View File

@ -0,0 +1,181 @@
// Copyright 2015-2018 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.
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "esp_modem.h"
#include "esp_log.h"
static const char *TAG = "esp-modem-netif";
/**
* @brief ESP32 Modem handle to be used as netif IO object
*/
typedef struct esp_modem_netif_driver_s {
esp_netif_driver_base_t base; /*!< base structure reserved as esp-netif driver */
modem_dte_t *dte; /*!< ptr to the esp_modem objects (DTE) */
} esp_modem_netif_driver_t;
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
modem_dte_t *dte = arg;
if (event_id < NETIF_PP_PHASE_OFFSET) {
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
// only notify the modem on state/error events, ignoring phase transitions
esp_modem_notify_ppp_netif_closed(dte);
}
}
/**
* @brief Transmit function called from esp_netif to output network stack data
*
* Note: This API has to conform to esp-netif transmit prototype
*
* @param h Opaque pointer representing esp-netif driver, esp_dte in this case of esp_modem
* @param data data buffer
* @param length length of data to send
*
* @return ESP_OK on success
*/
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len)
{
modem_dte_t *dte = h;
if (dte->send_data(dte, (const char *)buffer, len) > 0) {
return ESP_OK;
}
return ESP_FAIL;
}
/**
* @brief Post attach adapter for esp-modem
*
* Used to exchange internal callbacks, context between esp-netif nad modem-netif
*
* @param esp_netif handle to esp-netif object
* @param args pointer to modem-netif driver
*
* @return ESP_OK on success, modem-start error code if starting failed
*/
static esp_err_t esp_modem_post_attach_start(esp_netif_t * esp_netif, void * args)
{
esp_modem_netif_driver_t *driver = args;
modem_dte_t *dte = driver->dte;
const esp_netif_driver_ifconfig_t driver_ifconfig = {
.driver_free_rx_buffer = NULL,
.transmit = esp_modem_dte_transmit,
.handle = dte
};
driver->base.netif = esp_netif;
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
// enable both events, so we could notify the modem layer if an error occurred/state changed
esp_netif_ppp_config_t ppp_config = {
.ppp_error_event_enabled = true,
.ppp_phase_event_enabled = true
};
esp_netif_ppp_set_params(esp_netif, &ppp_config);
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, dte));
return esp_modem_start_ppp(dte);
}
/**
* @brief Data path callback from esp-modem to pass data to esp-netif
*
* @param buffer data pointer
* @param len data length
* @param context context data used for esp-modem-netif handle
*
* @return ESP_OK on success
*/
static esp_err_t modem_netif_receive_cb(void *buffer, size_t len, void *context)
{
esp_modem_netif_driver_t *driver = context;
esp_netif_receive(driver->base.netif, buffer, len, NULL);
return ESP_OK;
}
void *esp_modem_netif_setup(modem_dte_t *dte)
{
esp_modem_netif_driver_t *driver = calloc(1, sizeof(esp_modem_netif_driver_t));
if (driver == NULL) {
ESP_LOGE(TAG, "Cannot allocate esp_modem_netif_driver_t");
goto drv_create_failed;
}
esp_err_t err = esp_modem_set_rx_cb(dte, modem_netif_receive_cb, driver);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_rx_cb failed with: %d", err);
goto drv_create_failed;
}
driver->base.post_attach = esp_modem_post_attach_start;
driver->dte = dte;
return driver;
drv_create_failed:
return NULL;
}
void esp_modem_netif_teardown(void *h)
{
esp_modem_netif_driver_t *driver = h;
free(driver);
}
esp_err_t esp_modem_netif_clear_default_handlers(void *h)
{
esp_modem_netif_driver_t *driver = h;
esp_err_t ret;
ret = esp_modem_remove_event_handler(driver->dte, esp_netif_action_start);
if (ret != ESP_OK) {
goto clear_event_failed;
}
ret = esp_modem_remove_event_handler(driver->dte, esp_netif_action_stop);
if (ret != ESP_OK) {
goto clear_event_failed;
}
return ESP_OK;
clear_event_failed:
ESP_LOGE(TAG, "Failed to unregister event handlers");
return ESP_FAIL;
}
esp_err_t esp_modem_netif_set_default_handlers(void *h, esp_netif_t * esp_netif)
{
esp_modem_netif_driver_t *driver = h;
esp_err_t ret;
ret = esp_modem_set_event_handler(driver->dte, esp_netif_action_start, ESP_MODEM_EVENT_PPP_START, esp_netif);
if (ret != ESP_OK) {
goto set_event_failed;
}
ret = esp_modem_set_event_handler(driver->dte, esp_netif_action_stop, ESP_MODEM_EVENT_PPP_STOP, esp_netif);
if (ret != ESP_OK) {
goto set_event_failed;
}
ret = esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, esp_netif);
if (ret != ESP_OK) {
goto set_event_failed;
}
ret = esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected, esp_netif);
if (ret != ESP_OK) {
goto set_event_failed;
}
return ESP_OK;
set_event_failed:
ESP_LOGE(TAG, "Failed to register event handlers");
esp_modem_netif_clear_default_handlers(driver);
return ESP_FAIL;
}

90
esp_modem/src/sim7600.c Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2020 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.
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "bg96.h"
#include "bg96_private.h"
/**
* @brief This module supports SIM7600 module, which has a very similar interface
* to the BG96, so it just references most of the handlers from BG96 and implements
* only those that differ.
*/
static const char *DCE_TAG = "sim7600";
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t sim7600_handle_cbc(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
/* store value of bcs, bcl, voltage */
int32_t **cbc = bg96_dce->priv_resource;
int32_t volts = 0, fraction = 0;
/* +CBC: <voltage in Volts> V*/
sscanf(line, "+CBC: %d.%dV", &volts, &fraction);
/* Since the "read_battery_status()" API (besides voltage) returns also values for BCS, BCL (charge status),
* which are not applicable to this modem, we return -1 to indicate invalid value
*/
*cbc[0] = -1; // BCS
*cbc[1] = -1; // BCL
*cbc[2] = volts*1000 + fraction;
err = ESP_OK;
}
return err;
}
/**
* @brief Get battery status
*
* @param dce Modem DCE object
* @param bcs Battery charge status
* @param bcl Battery connection level
* @param voltage Battery voltage
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim7600_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
{
modem_dte_t *dte = dce->dte;
bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
uint32_t *resource[3] = {bcs, bcl, voltage};
bg96_dce->priv_resource = resource;
dce->handle_line = sim7600_handle_cbc;
DCE_CHECK(dte->send_cmd(dte, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire battery status failed", err);
ESP_LOGD(DCE_TAG, "inquire battery status ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Create and initialize SIM7600 object
*
*/
modem_dce_t *sim7600_init(modem_dte_t *dte)
{
modem_dce_t *dce = bg96_init(dte);
dte->dce->get_battery_status = sim7600_get_battery_status;
return dce;
}

492
esp_modem/src/sim800.c Normal file
View File

@ -0,0 +1,492 @@
// Copyright 2015-2018 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.
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_modem_dce_service.h"
#include "sim800.h"
#define MODEM_RESULT_CODE_POWERDOWN "POWER DOWN"
/**
* @brief Macro defined for error checking
*
*/
static const char *DCE_TAG = "sim800";
#define DCE_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
/**
* @brief SIM800 Modem
*
*/
typedef struct {
void *priv_resource; /*!< Private resource */
modem_dce_t parent; /*!< DCE parent class */
} sim800_modem_dce_t;
/**
* @brief Handle response from AT+CSQ
*/
static esp_err_t sim800_handle_csq(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CSQ", strlen("+CSQ"))) {
/* store value of rssi and ber */
uint32_t **csq = sim800_dce->priv_resource;
/* +CSQ: <rssi>,<ber> */
sscanf(line, "%*s%d,%d", csq[0], csq[1]);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t sim800_handle_cbc(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
/* store value of bcs, bcl, voltage */
uint32_t **cbc = sim800_dce->priv_resource;
/* +CBC: <bcs>,<bcl>,<voltage> */
sscanf(line, "%*s%d,%d,%d", cbc[0], cbc[1], cbc[2]);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from +++
*/
static esp_err_t sim800_handle_exit_data_mode(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Handle response from ATD*99#
*/
static esp_err_t sim800_handle_atd_ppp(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_CONNECT)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Handle response from AT+CGMM
*/
static esp_err_t sim800_handle_cgmm(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->name, MODEM_MAX_NAME_LENGTH, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->name, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+CGSN
*/
static esp_err_t sim800_handle_cgsn(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->imei, MODEM_IMEI_LENGTH + 1, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->imei, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+CIMI
*/
static esp_err_t sim800_handle_cimi(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else {
int len = snprintf(dce->imsi, MODEM_IMSI_LENGTH + 1, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->imsi, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+COPS?
*/
static esp_err_t sim800_handle_cops(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+COPS", strlen("+COPS"))) {
/* there might be some random spaces in operator's name, we can not use sscanf to parse the result */
/* strtok will break the string, we need to create a copy */
size_t len = strlen(line);
char *line_copy = malloc(len + 1);
strcpy(line_copy, line);
/* +COPS: <mode>[, <format>[, <oper>]] */
char *str_ptr = NULL;
char *p[3];
uint8_t i = 0;
/* strtok will broke string by replacing delimiter with '\0' */
p[i] = strtok_r(line_copy, ",", &str_ptr);
while (p[i]) {
p[++i] = strtok_r(NULL, ",", &str_ptr);
}
if (i >= 3) {
int len = snprintf(dce->oper, MODEM_MAX_OPERATOR_LENGTH, "%s", p[2]);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(dce->oper, len);
err = ESP_OK;
}
}
free(line_copy);
}
return err;
}
/**
* @brief Handle response from AT+CPOWD=1
*/
static esp_err_t sim800_handle_power_down(modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_POWERDOWN)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
}
return err;
}
/**
* @brief Get signal quality
*
* @param dce Modem DCE object
* @param rssi received signal strength indication
* @param ber bit error ratio
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_signal_quality(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber)
{
modem_dte_t *dte = dce->dte;
sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
uint32_t *resource[2] = {rssi, ber};
sim800_dce->priv_resource = resource;
dce->handle_line = sim800_handle_csq;
DCE_CHECK(dte->send_cmd(dte, "AT+CSQ\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire signal quality failed", err);
ESP_LOGD(DCE_TAG, "inquire signal quality ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get battery status
*
* @param dce Modem DCE object
* @param bcs Battery charge status
* @param bcl Battery connection level
* @param voltage Battery voltage
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
{
modem_dte_t *dte = dce->dte;
sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
uint32_t *resource[3] = {bcs, bcl, voltage};
sim800_dce->priv_resource = resource;
dce->handle_line = sim800_handle_cbc;
DCE_CHECK(dte->send_cmd(dte, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire battery status failed", err);
ESP_LOGD(DCE_TAG, "inquire battery status ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Set Working Mode
*
* @param dce Modem DCE object
* @param mode woking mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
{
modem_dte_t *dte = dce->dte;
switch (mode) {
case MODEM_COMMAND_MODE:
dce->handle_line = sim800_handle_exit_data_mode;
vTaskDelay(pdMS_TO_TICKS(1000)); // spec: 1s delay for the modem to recognize the escape sequence
if (dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) != ESP_OK) {
// "+++" Could fail if we are already in the command mode.
// in that case we ignore the timout and re-sync the modem
ESP_LOGI(DCE_TAG, "Sending \"+++\" command failed");
dce->handle_line = esp_modem_dce_handle_response_default;
DCE_CHECK(dte->send_cmd(dte, "AT\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "sync failed", err);
} else {
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter command mode failed", err);
}
ESP_LOGD(DCE_TAG, "enter command mode ok");
dce->mode = MODEM_COMMAND_MODE;
break;
case MODEM_PPP_MODE:
dce->handle_line = sim800_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATD*99#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
if (dce->state != MODEM_STATE_SUCCESS) {
// Initiate PPP mode could fail, if we've already "dialed" the data call before.
// in that case we retry with "ATO" to just resume the data mode
ESP_LOGD(DCE_TAG, "enter ppp mode failed, retry with ATO");
dce->handle_line = sim800_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATO\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter ppp mode failed", err);
}
ESP_LOGD(DCE_TAG, "enter ppp mode ok");
dce->mode = MODEM_PPP_MODE;
break;
default:
ESP_LOGW(DCE_TAG, "unsupported working mode: %d", mode);
goto err;
break;
}
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Power down
*
* @param sim800_dce sim800 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_power_down(modem_dce_t *dce)
{
modem_dte_t *dte = dce->dte;
dce->handle_line = sim800_handle_power_down;
DCE_CHECK(dte->send_cmd(dte, "AT+CPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF) == ESP_OK, "send command failed", err);
DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "power down failed", err);
ESP_LOGD(DCE_TAG, "power down ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module name
*
* @param sim800_dce sim800 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_module_name(sim800_modem_dce_t *sim800_dce)
{
modem_dte_t *dte = sim800_dce->parent.dte;
sim800_dce->parent.handle_line = sim800_handle_cgmm;
DCE_CHECK(dte->send_cmd(dte, "AT+CGMM\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get module name failed", err);
ESP_LOGD(DCE_TAG, "get module name ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module IMEI number
*
* @param sim800_dce sim800 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_imei_number(sim800_modem_dce_t *sim800_dce)
{
modem_dte_t *dte = sim800_dce->parent.dte;
sim800_dce->parent.handle_line = sim800_handle_cgsn;
DCE_CHECK(dte->send_cmd(dte, "AT+CGSN\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get imei number failed", err);
ESP_LOGD(DCE_TAG, "get imei number ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get DCE module IMSI number
*
* @param sim800_dce sim800 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_imsi_number(sim800_modem_dce_t *sim800_dce)
{
modem_dte_t *dte = sim800_dce->parent.dte;
sim800_dce->parent.handle_line = sim800_handle_cimi;
DCE_CHECK(dte->send_cmd(dte, "AT+CIMI\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get imsi number failed", err);
ESP_LOGD(DCE_TAG, "get imsi number ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Get Operator's name
*
* @param sim800_dce sim800 object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
static esp_err_t sim800_get_operator_name(sim800_modem_dce_t *sim800_dce)
{
modem_dte_t *dte = sim800_dce->parent.dte;
sim800_dce->parent.handle_line = sim800_handle_cops;
DCE_CHECK(dte->send_cmd(dte, "AT+COPS?\r", MODEM_COMMAND_TIMEOUT_OPERATOR) == ESP_OK, "send command failed", err);
DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get network operator failed", err);
ESP_LOGD(DCE_TAG, "get network operator ok");
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Deinitialize SIM800 object
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on fail
*/
static esp_err_t sim800_deinit(modem_dce_t *dce)
{
sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
if (dce->dte) {
dce->dte->dce = NULL;
}
free(sim800_dce);
return ESP_OK;
}
modem_dce_t *sim800_init(modem_dte_t *dte)
{
DCE_CHECK(dte, "DCE should bind with a DTE", err);
/* malloc memory for sim800_dce object */
sim800_modem_dce_t *sim800_dce = calloc(1, sizeof(sim800_modem_dce_t));
DCE_CHECK(sim800_dce, "calloc sim800_dce failed", err);
/* Bind DTE with DCE */
sim800_dce->parent.dte = dte;
dte->dce = &(sim800_dce->parent);
/* Bind methods */
sim800_dce->parent.handle_line = NULL;
sim800_dce->parent.sync = esp_modem_dce_sync;
sim800_dce->parent.echo_mode = esp_modem_dce_echo;
sim800_dce->parent.store_profile = esp_modem_dce_store_profile;
sim800_dce->parent.set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
sim800_dce->parent.define_pdp_context = esp_modem_dce_define_pdp_context;
sim800_dce->parent.hang_up = esp_modem_dce_hang_up;
sim800_dce->parent.get_signal_quality = sim800_get_signal_quality;
sim800_dce->parent.get_battery_status = sim800_get_battery_status;
sim800_dce->parent.set_working_mode = sim800_set_working_mode;
sim800_dce->parent.power_down = sim800_power_down;
sim800_dce->parent.deinit = sim800_deinit;
/* Sync between DTE and DCE */
DCE_CHECK(esp_modem_dce_sync(&(sim800_dce->parent)) == ESP_OK, "sync failed", err_io);
/* Close echo */
DCE_CHECK(esp_modem_dce_echo(&(sim800_dce->parent), false) == ESP_OK, "close echo mode failed", err_io);
/* Get Module name */
DCE_CHECK(sim800_get_module_name(sim800_dce) == ESP_OK, "get module name failed", err_io);
/* Get IMEI number */
DCE_CHECK(sim800_get_imei_number(sim800_dce) == ESP_OK, "get imei failed", err_io);
/* Get IMSI number */
DCE_CHECK(sim800_get_imsi_number(sim800_dce) == ESP_OK, "get imsi failed", err_io);
/* Get operator name */
DCE_CHECK(sim800_get_operator_name(sim800_dce) == ESP_OK, "get operator name failed", err_io);
return &(sim800_dce->parent);
err_io:
free(sim800_dce);
err:
return NULL;
}