Added simple docs sketch

This commit is contained in:
David Cermak
2021-04-13 20:29:55 +02:00
parent 8b7df5b8c9
commit 17d3d9a794
16 changed files with 3010 additions and 88 deletions

View File

@ -1,79 +1,17 @@
# ESP MODEM # ESP MODEM
This component is used to communicate with modems in the command mode (using AT commands), as well as the data mode The `esp-modem` component is a managed component for `esp-idf` that could be used for communication with GSM/LTE modems
(over PPPoS protocol). that support AT commands and PPP protocol as a network interface.
The modem device is modeled with a DCE (Data Communication Equipment) object, which is composed of:
* DTE (Data Terminal Equipment), which abstracts the terminal (currently only UART implemented).
* PPP Netif representing a network interface communicating with the DTE using PPP protocol.
* Module abstracting the specific device model and its commands.
``` ## Examples
+-----+
| DTE |--+
+-----+ | +-------+
+-->| DCE |
+-------+ | |o--- set_mode(command/data)
| Module|--->| |
+-------+ | |o--- send_commands
+->| |
+------+ | +-------+
| PPP |--+
| netif|------------------> network events
+------+
```
## Modem components Get started with one of the examples:
### DCE * `examples/pppos_client` -- simple client implemented in C
* `examples/modem_console` -- C++ example implementing simple terminal console with DCE commands
This is the basic operational unit of the esp_modem component, abstracting a specific module in software,
which is basically configured by
* the I/O communication media (UART), defined by the DTE configuration
* the specific command library supported by the device model, defined with the module type
* network interface configuration (PPPoS config in lwip)
After the object is created, the application interaction with the DCE is in
* issuing specific commands to the modem
* switching between data and command mode
### DTE
Is an abstraction of the connected interface. Current implementation supports only UART
### PPP
Is used to connect the specific network interface to the modem data mode. Currently implementation supports only PPPoS protocol.
### Module
Abstraction of the specific modem device. Currently the component supports SIM800, BG96, SIM7600.
## Use cases
Users could interact with the esp-modem using the DCE's interface, to basically
* Switch between command and data mode to connect to the internet via cellular network.
* Send various commands to the device (e.g. send SMS)
The applications typically register handlers for network events to receive notification on the network availability and
IP address changes.
Common use cases of the esp-modem are also listed as the examples:
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
* `examples/modem_console` -- is an example to exercise all possible modules commands in a console application.
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP * `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP
that forwards packets (and uses NAT) to and from the PPPoS connection. that forwards packets (and uses NAT) to and from the PPPoS connection.
## Extensibility ## Documentation
### CMUX * Continue with esp-modem [brief overview](docs/README.md)
* View the full [html documentation ](docs/html/index.html)
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
after creating multiple virtual terminals, designating some of them solely to the data mode, while other to command mode.
### DTE's
Currently we support only UART, but modern modules support other communication interfaces, such as USB, SPI.
### Other devices
Adding a new device is a must-have requirement for the esp-component. Different modules support different commands,
or some commands might have a different implementation. Adding a new device means to provide a new implementation
as a class derived from `GenericModule`, where we could add new commands or modify the existing ones.

2567
esp_modem/docs/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

79
esp_modem/docs/README.md Normal file
View File

@ -0,0 +1,79 @@
# ESP MODEM
This component is used to communicate with modems in the command mode (using AT commands), as well as the data mode
(over PPPoS protocol).
The modem device is modeled with a DCE (Data Communication Equipment) object, which is composed of:
* DTE (Data Terminal Equipment), which abstracts the terminal (currently only UART implemented).
* PPP Netif representing a network interface communicating with the DTE using PPP protocol.
* Module abstracting the specific device model and its commands.
```
+-----+
| DTE |--+
+-----+ | +-------+
+-->| DCE |
+-------+ | |o--- set_mode(command/data)
| Module|--->| |
+-------+ | |o--- send_commands
+->| |
+------+ | +-------+
| PPP |--+
| netif|------------------> network events
+------+
```
## Modem components
### DCE
This is the basic operational unit of the esp_modem component, abstracting a specific module in software,
which is basically configured by
* the I/O communication media (UART), defined by the DTE configuration
* the specific command library supported by the device model, defined with the module type
* network interface configuration (PPPoS config in lwip)
After the object is created, the application interaction with the DCE is in
* issuing specific commands to the modem
* switching between data and command mode
### DTE
Is an abstraction of the connected interface. Current implementation supports only UART
### PPP
Is used to connect the specific network interface to the modem data mode. Currently implementation supports only PPPoS protocol.
### Module
Abstraction of the specific modem device. Currently the component supports SIM800, BG96, SIM7600.
## Use cases
Users could interact with the esp-modem using the DCE's interface, to basically
* Switch between command and data mode to connect to the internet via cellular network.
* Send various commands to the device (e.g. send SMS)
The applications typically register handlers for network events to receive notification on the network availability and
IP address changes.
Common use cases of the esp-modem are also listed as the examples:
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
* `examples/modem_console` -- is an example to exercise all possible modules commands in a console application.
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP
that forwards packets (and uses NAT) to and from the PPPoS connection.
## Extensibility
### CMUX
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
after creating multiple virtual terminals, designating some of them solely to the data mode, while other to command mode.
### DTE's
Currently we support only UART, but modern modules support other communication interfaces, such as USB, SPI.
### Other devices
Adding a new device is a must-have requirement for the esp-component. Different modules support different commands,
or some commands might have a different implementation. Adding a new device means to provide a new implementation
as a class derived from `GenericModule`, where we could add new commands or modify the existing ones.

View File

@ -0,0 +1,15 @@
Advanced esp-modem use cases
============================
Create custom modem instance with DCE factory
---------------------------------------------
Create custom module
--------------------
Create new communication interface
----------------------------------

View File

@ -0,0 +1,40 @@
.. _c_api:
API Guide for C interface
=========================
C API is very simple and consist of these two basic parts:
- :ref:`lifecycle_api`
- :ref:`modem_commands`
Typical application workflow is to:
- Create a DCE instance (using :cpp:func:`esp_modem_new`)
- Call specific functions to issue AT commands (:ref:`modem_commands`)
- Switch to the data mode (using :cpp:func:`esp_modem_set_mode`)
- Perform desired network operations (using standard networking API, unrelated to ESP-MODEM)
- Optionally switch back to command mode (again :cpp:func:`esp_modem_set_mode`)
- Destroy the DCE handle (sing :cpp:func:`esp_modem_destroy`)
.. _lifecycle_api:
Lifecycle API
-------------
These functions are used to create, destroy and set modem working mode.
.. doxygengroup:: ESP_MODEM_C_API
.. _modem_commands:
Modem commands
--------------
These functions are the actual commands to communicate with the modem using AT command interface.
.. doxygenfile:: c_api.h

50
esp_modem/docs/c_api.h Normal file
View File

@ -0,0 +1,50 @@
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > c_api.h
// --- DCE command documentation starts here ---
/**
* @brief Sends the supplied PIN code
*
* @param pin Pin
*
*/ command_result esp_modem_set_pin (const char* pin); /**
* @brief Checks if the SIM needs a PIN
*
* @param[out] pin_ok Pin
*/ command_result esp_modem_read_pin (bool* pin_ok); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_set_echo (const bool x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_resume_data_mode (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_set_pdp_context (struct PdpContext* x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_set_command_mode (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_set_cmux (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_get_imsi (char* x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_get_imei (char* x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result esp_modem_get_module_name (char* name); /**
* @brief Sets the modem to data mode
*
*/ command_result esp_modem_set_data_mode (); /**
* @brief Get Signal quality
*
*/ command_result esp_modem_get_signal_quality (int* x, int* y);

27
esp_modem/docs/conf.py Normal file
View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
import os
import os.path
import re
import subprocess
import sys
# General information about the project.
project = u'ESP-MODEM Documentation'
copyright = u'2016 - 2021, Espressif Systems (Shanghai) Co., Ltd'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'
extensions = ['breathe' ]
breathe_projects = {'esp_modem': 'xml'}
breathe_default_project = "esp_modem"
source_suffix = ['.rst', '.md']
source_parsers = {'.md': 'recommonmark.parser.CommonMarkParser', }

60
esp_modem/docs/cpp_api.h Normal file
View File

@ -0,0 +1,60 @@
// --- DCE command documentation starts here ---
class DCE: public DCE_T<GenericModule> {
public:
using DCE_T<GenericModule>::DCE_T;
/**
* @brief Sends the supplied PIN code
*
* @param pin Pin
*
*/ command_result set_pin (const std::string& pin); /**
* @brief Checks if the SIM needs a PIN
*
* @param[out] pin_ok Pin
*/ command_result read_pin (bool& pin_ok); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result set_echo (const bool x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result resume_data_mode (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result set_pdp_context (PdpContext& x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result set_command_mode (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result set_cmux (); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result get_imsi (std::string& x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result get_imei (std::string& x); /**
* @brief Reads the module name
*
* @param[out] name module name
*/ command_result get_module_name (std::string& name); /**
* @brief Sets the modem to data mode
*
*/ command_result set_data_mode (); /**
* @brief Get Signal quality
*
*/ command_result get_signal_quality (int& x, int& y);
};

View File

@ -0,0 +1,39 @@
C++ API Documentation
=====================
Similar to the :ref:`c_api`, the basic application workflow consist of
- Construction of the DCE
- Sending (AT) commands
- Switching modes
- Destroying the DCE
Create DTE and DCE
------------------
.. doxygengroup:: ESP_MODEM_INIT_DTE
.. doxygengroup:: ESP_MODEM_INIT_DCE
Mode switching commands
-----------------------
.. doxygenclass:: esp_modem::DCE_T
:members:
Modem commands
--------------
Create the DCE object with DCE factory :cpp:func:`esp_modem_new`
.. doxygenclass:: DCE
:members:
Destroy the DCE
---------------
The DCE object is created as ``std::unique_ptr`` by default and as such doesn't have to be explicitly destroyed.
It simply gets destroyed and cleaned-up automatically if the object goes out of the block scope.

2
esp_modem/docs/generate_docs Executable file
View File

@ -0,0 +1,2 @@
python -u -m sphinx.cmd.build -b html . html

11
esp_modem/docs/index.rst Normal file
View File

@ -0,0 +1,11 @@
ESP-MODEM Documentation
=======================
.. toctree::
Brief intro <README>
C interface <api_docs>
C++ interface <cxx_api_docs>
Advanced use cases <advanced_api>
Internal design <internal_design>

View File

@ -44,6 +44,9 @@ public:
~DCE_T() = default; ~DCE_T() = default;
/**
* @brief Set data mode!
*/
void set_data() { set_mode(modem_mode::DATA_MODE); } void set_data() { set_mode(modem_mode::DATA_MODE); }
void exit_data() { set_mode(modem_mode::COMMAND_MODE); } void exit_data() { set_mode(modem_mode::COMMAND_MODE); }

View File

@ -24,17 +24,53 @@ extern "C" {
typedef struct esp_modem_dce_wrap esp_modem_dce_t; typedef struct esp_modem_dce_wrap esp_modem_dce_t;
struct PdpContext; struct PdpContext;
/**
* @defgroup ESP_MODEM_C_API ESP_MODEM C API
* @brief Set of basic C API for ESP-MODEM
*/
/** @addtogroup ESP_MODEM_C_API
* @{
*/
/**
* @brief DCE mode: This enum is used to set desired operation mode of the DCE
*/
typedef enum esp_modem_dce_mode typedef enum esp_modem_dce_mode
{ {
ESP_MODEM_MODE_COMMAND, ESP_MODEM_MODE_COMMAND, /**< Default mode after modem startup, used for sending AT commands */
ESP_MODEM_MODE_DATA, ESP_MODEM_MODE_DATA, /**< Used for switching to PPP mode for the modem to connect to a network */
} esp_modem_dce_mode_t; } esp_modem_dce_mode_t;
/**
* @brief Create a DCE handle for new modem API
*
* @param dte_config DTE configuration (UART config for now)
* @param dce_config DCE configuration
* @param netif Network interface handle for the data mode
*
* @return DCE pointer on success, NULL on failure
*/
esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif); esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif);
/**
* @brief Destroys modem's DCE handle
*
* @param dce DCE to destroy
*/
void esp_modem_destroy(esp_modem_dce_t * dce); void esp_modem_destroy(esp_modem_dce_t * dce);
/**
* @brief Set operation mode for this DCE
* @param dce Modem DCE handle
* @param mode Desired MODE
* @return ESP_OK on success, ESP_FAIL on failure
*/
esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce, esp_modem_dce_mode_t mode); esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce, esp_modem_dce_mode_t mode);
/**
* @}
*/
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -15,12 +15,18 @@
#ifndef _ESP_MODEM_COMMAND_DECLARE_INC_ #ifndef _ESP_MODEM_COMMAND_DECLARE_INC_
#define _ESP_MODEM_COMMAND_DECLARE_INC_ #define _ESP_MODEM_COMMAND_DECLARE_INC_
#if GENERATE_DOCS
#define _ARG(arg) arg
#else
#define _ARG(arg) x
#endif
#ifdef __cplusplus #ifdef __cplusplus
#include <string> #include <string>
#define STRING_IN(x) const std::string& x #define STRING_IN(x) const std::string& _ARG(x)
#define STRING_OUT(x) std::string& x #define STRING_OUT(x) std::string& _ARG(x)
#define BOOL_IN(x) const bool x #define BOOL_IN(x) const bool x
#define BOOL_OUT(x) bool& x #define BOOL_OUT(x) bool& _ARG(x)
#define INT_OUT(x) int& x #define INT_OUT(x) int& x
#define STRUCT_OUT(struct_name, x) struct_name& x #define STRUCT_OUT(struct_name, x) struct_name& x
@ -42,36 +48,74 @@
* @brief Sends the supplied PIN code * @brief Sends the supplied PIN code
* *
* @param pin Pin * @param pin Pin
*
*/ \ */ \
\ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, MUX_ARG, STRING_IN(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, MUX_ARG, STRING_IN(pin)) \
\
\
/** /**
* @brief Checks if the SIM needs a PIN * @brief Checks if the SIM needs a PIN
* *
* @param[out] pin_ok Pin * @param[out] pin_ok Pin
*/ \ */ \
ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, 1, MUX_ARG, BOOL_OUT(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, 1, MUX_ARG, BOOL_OUT(pin_ok)) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, 1, MUX_ARG, BOOL_IN(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, 1, MUX_ARG, BOOL_IN(x)) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, 0, MUX_ARG) \ ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, 0, MUX_ARG) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, 1, MUX_ARG, STRUCT_OUT(PdpContext, x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, 1, MUX_ARG, STRUCT_OUT(PdpContext, x)) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_command_mode, command_result, 0, MUX_ARG) \ ESP_MODEM_DECLARE_DCE_COMMAND(set_command_mode, command_result, 0, MUX_ARG) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result, 0, MUX_ARG) \ ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result, 0, MUX_ARG) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, 1, MUX_ARG, STRING_OUT(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, 1, MUX_ARG, STRING_OUT(x)) \
\ \
/**
* @brief Reads the module name
*
* @param[out] name module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, 1, MUX_ARG, STRING_OUT(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, 1, MUX_ARG, STRING_OUT(x)) \
\ \
/** /**
* @brief Reads the module name * @brief Reads the module name
* *
* @param[out] module name * @param[out] name module name
*/ \ */ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, 1, MUX_ARG, STRING_OUT(x)) \ ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, 1, MUX_ARG, STRING_OUT(name)) \
\ \
/** /**
* @brief Sets the modem to data mode * @brief Sets the modem to data mode
@ -85,15 +129,26 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_data_mode, command_result, 0, MUX_ARG) \
*/ \ */ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, MUX_ARG, INT_OUT(x), INT_OUT(y)) ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, MUX_ARG, INT_OUT(x), INT_OUT(y))
// --- DCE command documentation starts here ---
#ifdef GENERATE_DOCS #ifdef GENERATE_DOCS
// gcc -E -CC -P -DGENERATE_DOCS esp_modem_command_declare.inc | sed -n '/DCE command documentation/,//p' // cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p'
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > c_api.h
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, MUX_ARG, ...) \ // --- DCE command documentation starts here ---
return_type name (__VA_ARGS__); #ifdef __cplusplus
class DCE: public DCE_T<GenericModule> {
DECLARE_ALL_COMMAND_APIS() public:
using DCE_T<GenericModule>::DCE_T;
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, MUX_ARG, ...) return_type name (__VA_ARGS__);
#else
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, MUX_ARG, ...) return_type esp_modem_ ## name (__VA_ARGS__);
#endif #endif
DECLARE_ALL_COMMAND_APIS()
#ifdef __cplusplus
};
#endif
#endif
#endif // _ESP_MODEM_COMMAND_DECLARE_INC_ #endif // _ESP_MODEM_COMMAND_DECLARE_INC_

View File

@ -53,7 +53,7 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
// check if PPP error events are enabled, if not, do enable the error occurred/state changed // check if PPP error events are enabled, if not, do enable the error occurred/state changed
// to notify the modem layer when switching modes // to notify the modem layer when switching modes
esp_netif_ppp_config_t ppp_config; esp_netif_ppp_config_t ppp_config;
esp_netif_ppp_get_params(esp_netif, &ppp_config); // esp_netif_ppp_get_params(esp_netif, &ppp_config);
if (!ppp_config.ppp_error_event_enabled) { if (!ppp_config.ppp_error_event_enabled) {
ppp_config.ppp_error_event_enabled = true; ppp_config.ppp_error_event_enabled = true;
esp_netif_ppp_set_params(esp_netif, &ppp_config); esp_netif_ppp_set_params(esp_netif, &ppp_config);