Compare commits

..

112 Commits

Author SHA1 Message Date
80c3cf0f02 websocket: Initial version based on IDF 5.0 2022-03-02 15:48:43 +04:00
b3c777ad43 freertos: Remove legacy data types
This commit removes the usage of all legacy FreeRTOS data types that
are exposed via configENABLE_BACKWARD_COMPATIBILITY. Legacy types can
still be used by enabling CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY.


* Original commit: espressif/esp-idf@57fd78f5ba
2022-03-01 12:18:50 +04:00
8ce791e969 websocket: Added configs reconnect_timeout_ms and network_timeout_ms
Closes https://github.com/espressif/esp-idf/issues/8263


* Original commit: espressif/esp-idf@6c26d65203
2022-03-01 12:18:50 +04:00
fc7ed90d74 websocket: Updated Kconfig to use 'echo.websocket.events' echo server
Updated README to show how to run websocket echo server using Flask

Merges https://github.com/espressif/esp-idf/pull/8262


* Original commit: espressif/esp-idf@23598dfcec
2022-03-01 12:18:50 +04:00
59e82695e7 ws_client: Optimize example test payloads and timeouts
Important update: NO_DATA_TIMEOUT_SEC=5, as some ws-servers typically send pings in 30s or 10s intervals, so we might never fire shutdown test


* Original commit: espressif/esp-idf@323622be64
2022-03-01 12:18:50 +04:00
755f16222f ci/websockets: Run ws-client example test on ethernet runners
* Original commit: espressif/esp-idf@2649413ae8
2022-03-01 12:18:50 +04:00
bece6e7045 Add http_parser (new component) dependency
* Original commit: espressif/esp-idf@8e94cf2bb1
2022-03-01 12:18:50 +04:00
46bd32d952 websocket: removed deprecated API "esp_websocket_client_send"
Closes IDF-1470


* Original commit: espressif/esp-idf@7f6ab93f7e
2022-03-01 12:18:50 +04:00
525c70c0b2 refactor (test_utils)!: separate file for memory check functions
Memory check (leaks and heap tracing) functions for unit tests
now have a separate file now and are renamed for more consistency.

BREAKING CHANGE: renamed memory check function names which may be used
                 in unit tests outside IDF.


* Original commit: espressif/esp-idf@16514f93f0
2022-03-01 12:18:50 +04:00
c4c323666e docs: Fix spell and grammatical errors
- PCNT
- ESP-TLS
- ESP WebSocket Client


* Original commit: espressif/esp-idf@1df7f340be
2022-03-01 12:18:50 +04:00
19c0455b4d Build & config: Remove leftover files from the unsupported "make" build system
* Original commit: espressif/esp-idf@766aa57084
2022-03-01 12:18:50 +04:00
665c520faf [examples]: removed hyphens
Replaced hyphens with underscores in examples
project definition for all examples which had
hyphens in their project name. dpp-enrollee is
an exceptions because the name matches the
project directory name while the project
directory also contains hyphens.


* Original commit: espressif/esp-idf@81e9266204
2022-03-01 12:18:50 +04:00
9118e0f044 transport: Add CONFI_WS_TRANSPORT for optimize the code size
* Original commit: espressif/esp-idf@8b02c9026a
2022-03-01 12:18:50 +04:00
fbdbd550c0 ws_client: Fix const correctness in the API config structure
Merges https://github.com/espressif/esp-idf/pull/7113


* Original commit: espressif/esp-idf@70b1247a47
2022-03-01 12:18:50 +04:00
028be5a8d7 Split example_tests with Example_WIFI tag group into Example_OTA and Example_Protocols
* Original commit: espressif/esp-idf@0a395134d4
2022-03-01 12:18:50 +04:00
de7cd72f70 components: Remove repeated keep alive function by ssl layer function
In esp_http_client and esp_websocket_client components, esp_transport_tcp_set_keep_alive has been called and keep-alive config has been saved in ssl->cfg.keep_alive_cfg,
So no need to call esp_transport_ssl_set_keep_alive again.


* Original commit: espressif/esp-idf@c79a907e4f
2022-03-01 12:18:50 +04:00
4a608ec1cd components: Support bind socket to specified interface in esp_http_client and esp_websocket_client component
* Original commit: espressif/esp-idf@bead3599ab
2022-03-01 12:18:50 +04:00
f0351ff378 esp_websocket_client: Don't log the filename when logging "Websocket already stop"
Progress towards https://jira.espressif.com:8443/browse/IDFGH-4477


* Original commit: espressif/esp-idf@10bde42551
2022-03-01 12:18:50 +04:00
9219ff710a websocket: Add websocket unit tests
* Original commit: espressif/esp-idf@cd01a0ca81
2022-03-01 12:18:50 +04:00
86aa0b8d39 websockets: Set keepalive options after adding transport to the list
To be in line with other code and mainly to support base/foundation transport used by both tcp and ssl transport layers


* Original commit: espressif/esp-idf@99805d880f
2022-03-01 12:18:50 +04:00
1933367f63 websocket: Add configurable ping interval
Merges https://github.com/espressif/esp-idf/pull/6399

Signed-off-by: David Cermak <cermak@espressif.com>


* Original commit: espressif/esp-idf@9ff9137e7a
2022-03-01 12:18:50 +04:00
cf697a1a1b style: format python files with isort and double-quote-string-fixer
* Original commit: espressif/esp-idf@0146f258d7
2022-03-01 12:18:50 +04:00
95cf983502 ws_transport: Add option to propagate control packets to the app
Client could choose if they want to receive control packets and handle
them.
* If disabled (default) the transport itself tries to handle PING
and CLOSE frames automatically during read operation. If handled
correctly, read outputs 0 indicating no (actual app) data received.
* if enabled, all control frames are passed to the application to be
  processed there.

Closes https://github.com/espressif/esp-idf/issues/6307


* Original commit: espressif/esp-idf@acc7bd2ca4
2022-03-01 12:18:50 +04:00
8a6c320a29 Add options for esp_http_client and esp_websocket_client to support keepalive
* Original commit: espressif/esp-idf@b53e46a68e
2022-03-01 12:18:50 +04:00
d1dd6ece38 websocket: support mutual tls for websocket
Closes https://github.com/espressif/esp-idf/issues/6059


* Original commit: espressif/esp-idf@5ab774f9d8
2022-03-01 12:18:50 +04:00
36167db336 test: remove fake binary size check in example test:
the binary size check in example test was removed long time ago. Now we
have updated ttfw_idf to raise exception when performance standard is
not found. These fake performance check will be regarded as error.
Remove them now.


* Original commit: espressif/esp-idf@a908174c06
2022-03-01 12:18:50 +04:00
d376480766 Whitespace: Automated whitespace fixes (large commit)
Apply the pre-commit hook whitespace fixes to all files in the repo.

(Line endings, blank lines at end of file, trailing whitespace)


* Original commit: espressif/esp-idf@66fb5a29bb
2022-03-01 12:18:50 +04:00
e90272c812 Websocket client: avoid deadlock if stop called from event handler
* Original commit: espressif/esp-idf@c2bb0762bb
2022-03-01 12:18:50 +04:00
fda070ba39 ws_client tests: Updated example test to use WebsSocket package
Added a new test for closing connection with close frames


* Original commit: espressif/esp-idf@44c553fd14
2022-03-01 12:18:50 +04:00
6d12d06605 tcp_transport: Added internal API for underlying socket, used for custom select on connection end for WS
Internal tcp_transport functions could now use custom socket operations.
This is used for WebSocket transport, when we typically wait for clean
connection closure, i.e. selecting for read/error with expected errno or
recv size=0 while socket readable (=connection terminated by FIN flag)


* Original commit: espressif/esp-idf@5e9f8b52e7
2022-03-01 12:18:50 +04:00
1455bc0305 ws_client: Added support for close frame, closing connection gracefully
* Original commit: espressif/esp-idf@b213f2c6d3
2022-03-01 12:18:50 +04:00
01b4f640d9 driver, http_client, web_socket, tcp_transport: remove __FILE__ from log messages
__FILE__ macro in the error messages adds full paths to the production binarys, remove __FILE__ from the ESP_LOGE.

Closes https://github.com/espressif/esp-idf/issues/5637
Merges https://github.com/espressif/esp-idf/pull/5638


* Original commit: espressif/esp-idf@caaf62bdad
2022-03-01 12:18:50 +04:00
6ab0aea841 websocket_client : fix some issues for websocket client
1. will post twice disconnect event when read error
2. will block `timeout` times when set disable_auto_connect
3. When `esp_websocket_client_stop` before `esp_websocket_client_send*`,
if the `esp_websocket_client_send*` fails, the status will change to
 'WEBSOCKET_STATE_WAIT_TIMEOUT', and the next `esp_websocket_client_start` will fail forever


* Original commit: espressif/esp-idf@341e480573
2022-03-01 12:18:50 +04:00
5f2a50f09f doc/websocket: updates the API reference for ESP WebSocket Client
* Original commit: espressif/esp-idf@09f240c1e1
2022-03-01 12:18:50 +04:00
b71c49c277 websocket: add configurable timeout for PONG not received
Closes IDF-1744


* Original commit: espressif/esp-idf@0049385850
2022-03-01 12:18:50 +04:00
f8e3ba7813 websocket client: the client now aborts the connection if send fails.
Closes IDF-1744


* Original commit: espressif/esp-idf@6bebfc84f3
2022-03-01 12:18:50 +04:00
7a5b2d5a7d ws_client: fix fragmented send setting proper opcodes
Previous implementation violated the RFC by having both the actual opcode and WS_FIN flag set for all fragments of a message.
Fixed by setting the opcode only for the first fragment and WS_FIN for the last one

Closes IDFGH-2938
Closes https://github.com/espressif/esp-idf/issues/4974


* Original commit: espressif/esp-idf@14992e62c5
2022-03-01 12:18:50 +04:00
42920d7fb5 Fix format string in websocket example
* Original commit: espressif/esp-idf@5288a797ef
2022-03-01 12:18:50 +04:00
2b044f2434 Add multi-target support for performance tests
* Original commit: espressif/esp-idf@15884eccf2
2022-03-01 12:18:50 +04:00
2b6022c85d examples: websocket example to send textual data with esp_websocket_client_send_text()
Closes https://github.com/espressif/esp-idf/issues/4640


* Original commit: espressif/esp-idf@e5650d1ed8
2022-03-01 12:18:50 +04:00
fae2343b19 docs: add new top-level docs builder that builds docs for a single chip
* Original commit: espressif/esp-idf@e6211c7864
2022-03-01 12:18:50 +04:00
17281a515e esp32: add implementation of esp_timer based on TG0 LAC timer
Closes: IDF-979


* Original commit: espressif/esp-idf@739eb05bb9
2022-03-01 12:18:50 +04:00
aec6a75d40 tcp_transport/ws_client: websockets now correctly handle messages longer than buffer
transport_ws can now be read multiple times in a row to read frames larger than the buffer.

Added reporting of total payload length and offset to the user in websocket_client.

Added local example test for long messages.

Closes IDF-1083


* Original commit: espressif/esp-idf@ffeda3003c
2022-03-01 12:18:50 +04:00
a6be8e2e3d websocket: added missing event data
user_context was missing from websocket event data, added. Also added the websocket client handle to the event data.

Removed  unused event data struct.

Closes: IDF-1271


* Original commit: espressif/esp-idf@7c0e3765ec
2022-03-01 12:18:50 +04:00
09453e4694 esp_netif, examples: esp_netif_init() moved into ESP_ERROR_CHECK()
esp_netif_init() returns standard esp_err_t error code (unlike tcpip_adapter init), so shall be checked for the return value
Also to make the initialization code more consistent.


* Original commit: espressif/esp-idf@31b2702387
2022-03-01 12:18:50 +04:00
3b0488cfdc websocket_client: added example_test with a local websocket server
- Added a example test that connects to a local python websocket server.
- Added readme for websocket_client example.

Closes IDF-907


* Original commit: espressif/esp-idf@67c5225c14
2022-03-01 12:18:50 +04:00
f21a2f32e0 test: update example and unit tests with new import roles:
tiny_test_fw is a python package now. import it using normal way.


* Original commit: espressif/esp-idf@c906e2afee
2022-03-01 12:18:50 +04:00
a48b0fafe8 Add User-Agent and additional headers to esp_websocket_client
Merges https://github.com/espressif/esp-idf/pull/4345


* Original commit: espressif/esp-idf@9200250f51
2022-03-01 12:18:50 +04:00
1fcc001ae8 ws_client: fix handling timeouts by websocket client.
tcp-transport component did not support wait forever. this update uses value of -1 to request this state.
websocket client uses timeouts in RTOS ticks. fixed recalculation to ms (including special value of -1) to use correctly tcp-transport component

Closes https://github.com/espressif/esp-idf/issues/4316


* Original commit: espressif/esp-idf@e1f982921a
2022-03-01 12:18:50 +04:00
a41e3383b3 examples: protocol examples which use common connection component
updated to use esp_netif_init instead of tcpip_adapter in initialization code


* Original commit: espressif/esp-idf@a49b934ef8
2022-03-01 12:18:50 +04:00
d0121b964d websocket_client: fix locking mechanism in ws-client task and when sending data
closes https://github.com/espressif/esp-idf/issues/4169


* Original commit: espressif/esp-idf@7c5011f411
2022-03-01 12:18:50 +04:00
f55d8391c9 ws_client: fix for not sending ping responses, updated to pass events also for PING and PONG messages, added interfaces to send both binary and text data
closes https://github.com/espressif/esp-idf/issues/3982


* Original commit: espressif/esp-idf@abf9345b85
2022-03-01 12:18:50 +04:00
fe26b734b5 Cosmetic Kconfig fixes
* Original commit: espressif/esp-idf@d3ed17acd7
2022-03-01 12:18:50 +04:00
f5a26c4d32 websocket_client: fix URI parsing to include also query part in websocket connection path
closes https://github.com/espressif/esp-idf/issues/4090


* Original commit: espressif/esp-idf@271e6c4c9c
2022-03-01 12:18:50 +04:00
23f6a1d46e ws_client: fixed posting to event loop with websocket timeout
Executing event loop `esp_event_loop_run()` with timeout causes delays in receiving events from user code. Fixed by removing the timeout to post synchronously.

closes https://github.com/espressif/esp-idf/issues/3957


* Original commit: espressif/esp-idf@50505068c4
2022-03-01 12:18:50 +04:00
2553d65e64 ws_client: added subprotocol configuration option to websocket client
closes https://github.com/espressif/esp-idf/issues/3893


* Original commit: espressif/esp-idf@de6ea396f1
2022-03-01 12:18:50 +04:00
67949f94f4 ws_client: fixed path config issue when ws server configured using host and path instead of uri
closes https://github.com/espressif/esp-idf/issues/3892


* Original commit: espressif/esp-idf@c0ba9e19fc
2022-03-01 12:18:50 +04:00
bfc88ab76c ws_client: fixed transport config option when server address configured as host, port, transport rather then uri
closes https://github.com/espressif/esp-idf/issues/3891


* Original commit: espressif/esp-idf@adee25d90e
2022-03-01 12:18:50 +04:00
343fbfdcc9 ci: limit example test to ESP32s
* Original commit: espressif/esp-idf@63329b169b
2022-03-01 12:18:50 +04:00
4d644954fe esp_wifi: wifi support new event mechanism
1. WiFi support new event mechanism
2. Update examples to use new event mechanism


* Original commit: espressif/esp-idf@003a9872b7
2022-03-01 12:18:50 +04:00
da74a4a489 tools: Mass fixing of empty prototypes (for -Wstrict-prototypes)
* Original commit: espressif/esp-idf@afbaf74007
2022-03-01 12:18:50 +04:00
f718676083 ws_client: fix double delete issue in ws client initialization
* Original commit: espressif/esp-idf@9b507c45c8
2022-03-01 12:18:50 +04:00
13a40d2344 ws_client: removed dependency on internal tcp_transport header
* Original commit: espressif/esp-idf@d1433564ec
2022-03-01 12:18:50 +04:00
35d6f9a2c6 examples: use new component registration api
* Original commit: espressif/esp-idf@6771eead80
2022-03-01 12:18:50 +04:00
f3a0586663 esp_websocket_client: Add websocket client component
Closes https://github.com/espressif/esp-idf/issues/2829


* Original commit: espressif/esp-idf@2a2d932cfe
2022-03-01 12:18:50 +04:00
325a1933c4 fix: missing default statement in a switch in the modem_console example 2022-01-28 22:34:03 +07:00
face03e4e5 fix: add virtual destructors to ModuleIf and CommandableIf 2022-01-28 22:34:03 +07:00
2cb74cf8d0 Update(esp_modem): Bump component version 2021-12-06 12:49:09 +01:00
d879e82a42 fix(esp_modem): Update the test to exersise CBC parser 2021-12-06 11:17:24 +01:00
4f1d31f9b7 fix(esp_modem): Fix battery status parse
Variable `pos`  was meant to hold position of first `,` (comma)
appearance in the parsed string, but due to wrong parantheses it
contained the boolean representation of not equal to `string::npos`
2021-12-06 11:17:24 +01:00
bece4efa09 Merge pull request #9 from david-cermak/bugfix/esp_modem_read_module_name
fix(esp_modem): Read module name with AT commands
2021-12-01 15:31:23 +01:00
8417e232aa fix(esp_modem): Read module name with AT commands
This fixes basic IDF test case which supplied dummy module name.
2021-11-25 16:24:52 +01:00
5f0832a0ad Merge pull request #7 from david-cermak/feature/ci_test_report
CI: Add run host to the CI
2021-11-18 15:42:33 +01:00
464baeeb83 Merge pull request #8 from sudeep-mohanty/master
esp_modem: Update FreeRTOS EventQueueHandle_t forward declaration
2021-11-18 12:13:17 +01:00
5d9ad9cffd esp_modem: Update FreeRTOS EventQueueHandle_t forward declaration
This commit includes freertos/event_groups.h header and removes the
forward declaration for EventGroupHandle_t.

Signed-off-by: Sudeep Mohanty <sudeep.mohanty@espressif.com>
2021-11-18 16:24:35 +05:30
9fbd6e658a CI: Build on linux and Run host tests 2021-11-18 08:35:55 +01:00
8465b14653 fix(esp_modem): linux port to work with lwip-2.1.2 2021-11-18 08:35:49 +01:00
e7ae0301ae Merge pull request #5 from david-cermak/bugfix/missign_c_api
fix(esp_modem): Add missing C API
2021-11-18 07:36:40 +01:00
96498760bf esp_modem: New version to publish to component registry 2021-11-16 08:13:26 +01:00
cf990d1a87 Docs: Add links to GitHub pages 2021-11-16 08:11:57 +01:00
5299b425e8 esp_modem: Add missing AT commands to plain C-API 2021-11-16 08:03:30 +01:00
bcb1ab99bd Merge pull request #4 from david-cermak/feature/build_docs
CI/Docs: Add jobs to deploy component docs
2021-11-16 07:56:50 +01:00
2c21aa1113 CI/Docs: Generate docs locally before uploading to registry 2021-11-16 07:51:24 +01:00
b9ff1e4e12 CI/Docs: Add jobs to deploy component docs 2021-11-12 16:51:21 +01:00
23dbdb584e Merge pull request #3 from david-cermak/feature/upload_components
Added actions to upload components and sync issues
2021-11-08 18:04:37 +01:00
1d0cc49c3f CI: Sync issues to JIRA 2021-11-08 15:18:44 +01:00
c1249aec17 CI: Upload comonents to Espressif Service on master
Co-authored-by: Sergei Silnov <po@kumekay.com>
2021-11-08 15:18:35 +01:00
2bbcb95e53 Merge pull request #2 from sudeep-mohanty/master
esp_modem: Update Task handle and Event Group handle to match freertos v10.4.3
2021-11-08 12:44:05 +01:00
58887170d2 esp_modem: Update Task handle and Event Group handle to match freertos v10.4.3
This commit updates the default handles for Task type and Signal Group
type to match the struct type handle from freertos v10.4.3.

Signed-off-by: Sudeep Mohanty <sudeep.mohanty@espressif.com>
2021-11-08 10:46:53 +05:30
cc7aa03a37 Merge pull request #1 from david-cermak/master
CI: Initial GitHub Action to build examples
2021-11-04 13:33:59 +01:00
15a858b735 CI: Move separate master job to the loop loop 2021-11-04 13:27:05 +01:00
01e2a9c109 Examples: Fix print format for http_client() API 2021-11-04 11:41:11 +01:00
c0021ceeb2 CI: Rewrite flat build jobs using test matrix 2021-11-04 11:33:28 +01:00
fec83e5915 CI: Initial GitHub Action to build examples
Build job definitions for building examples for ESP32 only against IDF
master, 4.4, 4.3, 4.2 and 4.1
2021-11-03 17:39:47 +01:00
c8c24ed2fc Examples: Fix build error when SIM needs no PIN 2021-11-03 13:59:24 +01:00
b380ded5fa Examples: Use bigger 4M flash for OTA example 2021-11-03 13:59:24 +01:00
38f6eb963a README: esp-protocol repository 2021-10-04 11:39:32 +02:00
2a2d27086f esp_modem(0.1.9): Fix CMake3.5 build with no cxx_std_17 feature
Older CMake (< 3.8, but still supported in IDF) doesn't support target_compile_features() for cxx_std_17.
If CMake doesn't know C++17 features, set it manually via comile options.
2021-10-04 11:35:27 +02:00
9dd1bd5ed0 esp_modem: 0.1.8: Update comonent version 2021-10-04 11:32:55 +02:00
918db0d2f1 Add pre upload script to deploy the docs before component upload 2021-10-04 11:32:55 +02:00
c1b1330680 esp_modem(Docs): Minor correction of the docs 2021-10-04 11:32:55 +02:00
fb7295e91a Examples/console: Remove unnecessary event-base check on IP handler 2021-10-04 11:32:55 +02:00
8236b3d490 Examples: Extend CMux client default stack size for the main task 2021-10-04 11:32:55 +02:00
fb6029b66c CMUX: Refactor the protocol decoder to multiple methods per cmux state 2021-10-04 11:32:55 +02:00
6e34954677 Factory: Rename Builder class to Creator, since it's not 100% builder pattern 2021-10-04 11:32:55 +02:00
148a9300a4 esp_modem: Formal updates per code review 2021-10-04 11:32:55 +02:00
1fb9150bcc Docs: Update CMux collaboration diagram to resemble composite 2021-10-04 11:32:55 +02:00
a61e9e2d40 Applied astyle code formatting 2021-10-04 11:32:55 +02:00
dc64f862c4 esp-modem(DCE-Factory): Minor corrections per code review 2021-10-04 11:32:55 +02:00
3332c27978 Bulk replace header guards to use 2021-10-04 11:32:55 +02:00
84b0dcfea4 Host test fix: renamed Loopback inherited member 2021-10-04 11:32:55 +02:00
e0e65856f0 esp-modem(Docs): Update documentation and minor fixes 2021-10-04 11:32:55 +02:00
116 changed files with 3953 additions and 1323 deletions

26
.github/workflows/build-websockets.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Build Websockets
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
path: esp-protocols
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_websocket_client/examples/
idf.py build

36
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Build
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
idf_ver: ["latest", "release-v4.1", "release-v4.2", "release-v4.3", "release-v4.4"]
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
idf_target: ["esp32"]
exclude:
- idf_ver: "release-v4.1"
example: modem_console
- idf_ver: "release-v4.1"
example: ap_to_pppos
- idf_ver: "release-v4.1"
example: simple_cmux_client
- idf_ver: "release-v4.2"
example: simple_cmux_client
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
path: esp-protocols
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_modem/examples/${{ matrix.example }}
idf.py build

44
.github/workflows/host-test.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: Host test
on: [push, pull_request]
jobs:
host_test:
name: Build and test
runs-on: ubuntu-20.04
container: espressif/idf:release-v4.3
env:
lwip: lwip-2.1.2
lwip_contrib: contrib-2.1.0
lwip_uri: http://download.savannah.nongnu.org/releases/lwip
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
path: esp-protocols
- name: Build and Test
shell: bash
run: |
apt-get update && apt-get install -y gcc-8 g++-8
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
export LWIP_PATH=`pwd`/${{ env.lwip }}
export LWIP_CONTRIB_PATH=`pwd`/${{ env.lwip_contrib }}
wget --no-verbose ${lwip_uri}/${lwip}.zip
unzip -oq ${lwip}.zip
wget --no-verbose ${lwip_uri}/${lwip_contrib}.zip
unzip -oq ${lwip_contrib}.zip
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_modem/examples/linux_modem
idf.py build
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_modem/test/host_test
idf.py build
./build/host_modem_test.elf -r junit -o junit.xml
- name: Publish Results
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: esp-protocols/components/esp_modem/test/host_test/junit.xml

View File

@ -0,0 +1,39 @@
name: Docs and Publish Websockets
on:
push:
branches:
- master
jobs:
docs_build:
name: Docs-Build-And-Upload
runs-on: ubuntu-latest
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
- name: Generate docs
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip git
sudo git clone https://github.com/espressif/esp-idf
python -m pip install breathe recommonmark
python -m pip install -r esp-idf/docs/requirements.txt
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a>' >> index.html
- name: Deploy generated docs.
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: docs

View File

@ -0,0 +1,45 @@
name: Docs and Publish
on:
push:
branches:
- master
jobs:
docs_build:
name: Docs-Build-And-Upload
runs-on: ubuntu-latest
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
- name: Generate docs
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark
cd $GITHUB_WORKSPACE/components/esp_modem/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_modem/index.html">esp-modem</a>' > index.html
- name: Upload components to component service
uses: espressif/github-actions/upload_components@master
with:
directories: "components/esp_modem"
name: "esp_modem"
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
- name: Deploy generated docs
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: docs

21
.github/workflows/sync_issues.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Sync issue comments to JIRA
# This workflow will be triggered when new issue is created
# or a new issue/PR comment is created
on: [issues, issue_comment]
jobs:
sync_issue_comments_to_jira:
name: Sync Issue Comments to Jira
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Sync issue comments to JIRA
uses: espressif/github-actions/sync_issues_to_jira@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JIRA_PASS: ${{ secrets.JIRA_PASS }}
JIRA_PROJECT: IDFGH
JIRA_COMPONENT: esp-protocols
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USER: ${{ secrets.JIRA_USER }}

View File

@ -1 +1,10 @@
# Collection of components for ESP-IDF
# Collection of protocol components for ESP-IDF
[Documentation of esp-protocol](https://espressif.github.io/esp-protocols)
## Components
### esp_modem
* Brief introduction [README](components/esp_modem/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)

View File

@ -7,6 +7,7 @@ if(${target} STREQUAL "linux")
set(dependencies esp_system_protocols_linux)
else()
set(platform_srcs src/esp_modem_primitives_freertos.cpp
src/esp_modem_api_target.cpp
src/esp_modem_uart.cpp
src/esp_modem_term_uart.cpp
src/esp_modem_netif.cpp)
@ -33,7 +34,16 @@ idf_component_register(SRCS "${srcs}"
PRIV_INCLUDE_DIRS private_include
REQUIRES ${dependencies})
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
# If CMake doesn't know C++17 features, set it manually via comile options
# esp-modem component requires C++17 internally, but older CMake (< 3.8, but still supported in IDF)
# doesn't support target_compile_features() for cxx_std_17.
get_property(cxx_known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
if ("cxx_std_17" IN_LIST cxx_known_features)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
else()
target_compile_options(${COMPONENT_LIB} PRIVATE "-std=gnu++17")
endif()
if(${target} STREQUAL "linux")
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64

View File

@ -1,6 +1,6 @@
# ESP MODEM
The `esp-modem` component is a managed component for `esp-idf` that could be used for communication with GSM/LTE modems
The `esp-modem` component is a managed component for `esp-idf` that is used for communication with GSM/LTE modems
that support AT commands and PPP protocol as a network interface.
## Examples
@ -16,4 +16,4 @@ Get started with one of the examples:
## Documentation
* Continue with esp-modem [brief overview](docs/README.md)
* View the full [html documentation ](docs/html/index.html)
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -36,11 +36,11 @@ After the object is created, the application interaction with the DCE is in
* switching between data and command mode
### DTE
Is an abstraction of the connected interface. Current implementation supports only UART
Is an abstraction of the physical interface connected to the modem. Current implementation supports only UART
### PPP
### PPP netif
Is used to connect the specific network interface to the modem data mode. Currently implementation supports only PPPoS protocol.
Is used to attach the specific network interface to a network communication protocol used by the modem. Currently implementation supports only PPPoS protocol.
### Module
@ -48,7 +48,7 @@ Abstraction of the specific modem device. Currently the component supports SIM80
## Use cases
Users could interact with the esp-modem using the DCE's interface, to basically
Users 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)
@ -57,16 +57,15 @@ 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.
* `examples/modem_console` -- is an example to exercise all possible module 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.
after creating multiple virtual terminals, designating some of them solely to data mode, others solely to command mode.
### DTE's
@ -74,6 +73,6 @@ Currently we support only UART, but modern modules support other communication i
### Other devices
Adding a new device is a must-have requirement for the esp-component. Different modules support different commands,
Adding a new device is a must-have requirement for the esp-modem 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

@ -19,14 +19,15 @@ All the functionality is provided by the DCE factory
.. doxygengroup:: ESP_MODEM_DCE_FACTORY
:members:
.. _create_custom_module:
Create custom module
--------------------
Creating a custom module is necessary if the application needs to use a specific device that is not supported
and their commands differ from any of the supported devices. In this case it is recommended to define a new class
representing this specific device and derive from the :cpp:class:`GenericModule`. In order to instantiate
the appropriate DCE of this module, application could use :ref:`the DCE factory<dce_factory>`, but build the DCE with
representing this specific device and derive from the :cpp:class:`esp_modem::GenericModule`. In order to instantiate
the appropriate DCE of this module, application could use :ref:`the DCE factory<dce_factory>`, and build the DCE with
the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`.
Please refer to the implementation of the existing modules.
@ -41,7 +42,7 @@ Create new communication interface
In order to connect to a device using an unsupported interface (e.g. SPI or I2C), it is necessary to implement
a custom DTE object and supply it into :ref:`the DCE factory<dce_factory>`. The DCE is typically created in two steps:
- Define and create the corresponding terminal, which can communicate on the custom interface. This terminal should support basic IO methods defined in :cpp:class:`esp_modem::Terminal` and derive from it.
- Define and create the corresponding terminal, which communicates on the custom interface. This terminal should support basic IO methods defined in :cpp:class:`esp_modem::Terminal` and derive from it.
- Create the DTE which uses the custom Terminal
Please refer to the implementation of the existing UART DTE.

View File

@ -1,15 +1,15 @@
.. _c_api:
API Guide for C interface
=========================
C API Documentation
===================
C API is very simple and consist of these two basic parts:
The C API is very simple and consist of these two basic parts:
- :ref:`lifecycle_api`
- :ref:`modem_commands`
Typical application workflow is to:
The 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`)
@ -41,6 +41,9 @@ Modem commands
These functions are the actual commands to communicate with the modem using AT command interface.
Note that the functions which implement AT commands returning textual values use plain ``char *``
pointer as the return value. The API expects the output data to point to user allocated space of at least
``ESP_MODEM_C_API_STR_MAX`` (64 by default) bytes, it also truncates the output data to this size.
.. doxygenfile:: esp_modem_api_commands.h

View File

@ -16,7 +16,8 @@ copyright = u'2016 - 2021, Espressif Systems (Shanghai) Co., Ltd'
# for a list of supported languages.
language = 'en'
extensions = ['breathe']
extensions = ['breathe', 'recommonmark']
breathe_projects = {'esp_modem': 'xml'}

View File

@ -1,25 +0,0 @@
- :cpp:func:`esp_modem::DCE::sync`
- :cpp:func:`esp_modem::DCE::get_operator_name`
- :cpp:func:`esp_modem::DCE::store_profile`
- :cpp:func:`esp_modem::DCE::set_pin`
- :cpp:func:`esp_modem::DCE::read_pin`
- :cpp:func:`esp_modem::DCE::set_echo`
- :cpp:func:`esp_modem::DCE::sms_txt_mode`
- :cpp:func:`esp_modem::DCE::sms_character_set`
- :cpp:func:`esp_modem::DCE::send_sms`
- :cpp:func:`esp_modem::DCE::resume_data_mode`
- :cpp:func:`esp_modem::DCE::set_pdp_context`
- :cpp:func:`esp_modem::DCE::set_command_mode`
- :cpp:func:`esp_modem::DCE::set_cmux`
- :cpp:func:`esp_modem::DCE::get_imsi`
- :cpp:func:`esp_modem::DCE::get_imei`
- :cpp:func:`esp_modem::DCE::get_module_name`
- :cpp:func:`esp_modem::DCE::set_data_mode`
- :cpp:func:`esp_modem::DCE::get_signal_quality`
- :cpp:func:`esp_modem::DCE::set_flow_control`
- :cpp:func:`esp_modem::DCE::hang_up`
- :cpp:func:`esp_modem::DCE::get_battery_status`
- :cpp:func:`esp_modem::DCE::power_down`
- :cpp:func:`esp_modem::DCE::reset`
- :cpp:func:`esp_modem::DCE::set_baud`

View File

@ -1,99 +0,0 @@
// 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
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
// call parametrs by names for documentation
// --- DCE command documentation starts here ---
/**
* @brief Sends the initial AT sequence to sync up with the device
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sync (); /**
* @brief Reads the operator name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_operator_name (char* name); /**
* @brief Stores current user profile
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_store_profile (); /**
* @brief Sets the supplied PIN code
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/command_result esp_modem_set_pin (const char* pin); /**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_read_pin (bool* pin_ok); /**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_echo (const bool echo_on); /**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sms_txt_mode (const bool txt); /**
* @brief Sets the default (GSM) charater set
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_sms_character_set (); /**
* @brief Sends SMS message in txt mode
* @param[in] number Phone number to send the message to
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_send_sms (const char* number, const char* message); /**
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_resume_data_mode (); /**
* @brief Sets php context
* @param[in] x PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_pdp_context (struct PdpContext* x); /**
* @brief Switches to the command mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_command_mode (); /**
* @brief Switches to the CMUX mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_cmux (); /**
* @brief Reads the IMSI number
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_imsi (char* imsi); /**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_imei (char* imei); /**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_module_name (char* name); /**
* @brief Sets the modem to data mode
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_data_mode (); /**
* @brief Get Signal quality
* @param[out] rssi signal strength indication
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_signal_quality (int* rssi, int* ber); /**
* @brief Sets HW control flow
* @param[in] dce_flow 0=none, 2=RTS hw flow control of DCE
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_flow_control (int dce_flow, int dte_flow); /**
* @brief Hangs up current data call
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_hang_up (); /**
* @brief Get voltage levels of modem power up circuitry
* @param[out] voltage Current status in mV
* @param[out] bcs charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_get_battery_status (int* voltage, int* bcs, int* bcl); /**
* @brief Power down the module
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_power_down (); /**
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_reset (); /**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/ command_result esp_modem_set_baud (int baud);

View File

@ -1,111 +0,0 @@
// 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
// cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
// call parametrs by names for documentation
// --- DCE command documentation starts here ---
class esp_modem::DCE: public DCE_T<GenericModule> {
public:
using DCE_T<GenericModule>::DCE_T;
/**
* @brief Sends the initial AT sequence to sync up with the device
* @return OK, FAIL or TIMEOUT
*/ command_result sync (); /**
* @brief Reads the operator name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result get_operator_name (std::string& name); /**
* @brief Stores current user profile
* @return OK, FAIL or TIMEOUT
*/ command_result store_profile (); /**
* @brief Sets the supplied PIN code
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/command_result set_pin (const std::string& pin); /**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/ command_result read_pin (bool& pin_ok); /**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/ command_result set_echo (const bool echo_on); /**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/ command_result sms_txt_mode (const bool txt); /**
* @brief Sets the default (GSM) charater set
* @return OK, FAIL or TIMEOUT
*/ command_result sms_character_set (); /**
* @brief Sends SMS message in txt mode
* @param[in] number Phone number to send the message to
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/ command_result send_sms (const std::string& number, const std::string& message); /**
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
* @return OK, FAIL or TIMEOUT
*/ command_result resume_data_mode (); /**
* @brief Sets php context
* @param[in] x PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/ command_result set_pdp_context (PdpContext& x); /**
* @brief Switches to the command mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_command_mode (); /**
* @brief Switches to the CMUX mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_cmux (); /**
* @brief Reads the IMSI number
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/ command_result get_imsi (std::string& imsi); /**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/ command_result get_imei (std::string& imei); /**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ command_result get_module_name (std::string& name); /**
* @brief Sets the modem to data mode
* @return OK, FAIL or TIMEOUT
*/ command_result set_data_mode (); /**
* @brief Get Signal quality
* @param[out] rssi signal strength indication
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/ command_result get_signal_quality (int& rssi, int& ber); /**
* @brief Sets HW control flow
* @param[in] dce_flow 0=none, 2=RTS hw flow control of DCE
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/ command_result set_flow_control (int dce_flow, int dte_flow); /**
* @brief Hangs up current data call
* @return OK, FAIL or TIMEOUT
*/ command_result hang_up (); /**
* @brief Get voltage levels of modem power up circuitry
* @param[out] voltage Current status in mV
* @param[out] bcs charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/ command_result get_battery_status (int& voltage, int& bcs, int& bcl); /**
* @brief Power down the module
* @return OK, FAIL or TIMEOUT
*/ command_result power_down (); /**
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/ command_result reset (); /**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/ command_result set_baud (int baud);
};

View File

@ -19,5 +19,5 @@ doxygen
# Generate the docs
python -u -m sphinx.cmd.build -b html . html
# Cleanup the doxygen xml's
rm -rf xml
# Cleanup the doxygen xml's and temporary headers
rm -rf xml esp_modem_api_commands.h esp_modem_dce.hpp cxx_api_links.rst

View File

@ -9,6 +9,13 @@ The esp-modem actually implements the DCE class, which in turn aggregates these
- :ref:`Netif<netif_impl>` to provide the network connectivity
- :ref:`Module<module_impl>` to define the specific command library
Developers would typically have to
* Add support for a new module
* Implement a generic (common for all modules) AT command
This is explained in the :ref:`Module<module_impl>` section, as :ref:`Adding new module or command<module_addition>`
------------
.. doxygengroup:: ESP_MODEM_DCE
@ -63,6 +70,36 @@ Module abstraction
.. doxygengroup:: ESP_MODEM_MODULE
:members:
.. _module_addition:
Adding new devices
^^^^^^^^^^^^^^^^^^
To support a new module, developers would have to implement a new class derived from :cpp:class:`esp_modem::GenericModule` the same way
as it is described in the :ref:`Advanced user manual<create_custom_module>`. The only difference is that the new class (and factory extension)
would be available in the esp_modem code base.
Implement a new generic command
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Adding a generic command, i.e. the command that is shared for all modules and is included in the :cpp:class:`esp_modem::GenericModule`,
has to be declared first in the ``include/generate/esp_modem_command_declare.inc`` file, which is the single source
of supported command definitions, that is used in:
* public C API
* public CPP API
* generated documentation
* implementation of the command
Therefore, a care must be taken, to correctly specify all parameters and types, especially:
* Keep number of parameters low (<= 6, used in preprocessor's forwarding to the command library)
* Use macros to specify parameter types (as they are used both from C and C++ with different underlying types)
* Parameter names are used only for clarity and documentation, they get expanded to numbered arguments.
Please use the following pattern: ``INT_IN(p1, baud)``, meaning that the parameter is an input integer,
human readable argument name is ``baud``, it's the first argument, so expands to ``p1`` (second argument would be ``p2``, etc)
Command library
^^^^^^^^^^^^^^^

View File

@ -31,9 +31,16 @@ menu "Example Configuration"
help
Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_NEED_SIM_PIN
bool "SIM PIN needed"
default n
help
Enable to set SIM PIN before starting the example
config EXAMPLE_SIM_PIN
string "Set SIM PIN"
default "1234"
depends on EXAMPLE_NEED_SIM_PIN
help
Pin to unlock the SIM

View File

@ -32,36 +32,32 @@ static const int DISCONNECT_BIT = BIT1;
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == IP_EVENT) {
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, 0, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, 1, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, 0, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, 1, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
xEventGroupSetBits(event_group, DISCONNECT_BIT);
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
xEventGroupSetBits(event_group, DISCONNECT_BIT);
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
@ -93,7 +89,6 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
void wifi_init_softap(void)
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

View File

@ -25,6 +25,7 @@ esp_err_t modem_init_network(esp_netif_t *netif)
return ESP_FAIL;
}
#ifdef CONFIG_EXAMPLE_NEED_SIM_PIN
// configure the PIN
bool pin_ok = false;
if (esp_modem_read_pin(dce, &pin_ok) == ESP_OK && pin_ok == false) {
@ -34,6 +35,7 @@ esp_err_t modem_init_network(esp_netif_t *netif)
abort();
}
}
#endif // CONFIG_EXAMPLE_NEED_SIM_PIN
return ESP_OK;
}

View File

@ -19,40 +19,28 @@ using namespace esp_modem;
using namespace esp_modem::dce_factory;
class NetModule;
typedef DCE_T<NetModule> NetDCE;
/**
* @brief Local network object used to setup PPP network
*/
class PPPNetwork {
public:
esp_err_t init(esp_netif_t *netif, const std::string& apn, const std::string &pin_number);
void deinit();
NetDCE * get_dce();
private:
NetDCE *dce;
};
using NetDCE = DCE_T<NetModule>;
/**
* @brief The PPP network is a singleton, allocate statically here
*/
static PPPNetwork ppp_network;
/**
* @brief Custom factory for creating NetDCE and NetModule
*/
class NetDCE_Factory: public Factory {
public:
template <typename T, typename ...Args>
static DCE_T<T>* create(const config *cfg, Args&&... args)
template <typename Module, typename ...Args>
static DCE_T<Module> *create(const config *cfg, Args &&... args)
{
return build_generic_DCE<T>(cfg, std::forward<Args>(args)...);
return build_generic_DCE<Module>(cfg, std::forward<Args>(args)...);
}
template <typename T, typename ...Args>
static std::shared_ptr<T> create_module(const config *cfg, Args&&... args)
template <typename Module, typename ...Args>
static std::shared_ptr<Module> create_module(const config *cfg, Args &&... args)
{
return build_shared_module<T>(cfg, std::forward<Args>(args)...);
return build_shared_module<Module>(cfg, std::forward<Args>(args)...);
}
};
@ -65,42 +53,48 @@ public:
class NetModule: public ModuleIf {
public:
explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg):
dte(std::move(dte)), apn(std::string(cfg->apn)) {}
dte(std::move(dte)), apn(std::string(cfg->apn)) {}
bool setup_data_mode() override
[[nodiscard]] bool setup_data_mode() override
{
PdpContext pdp(apn);
if (set_pdp_context(pdp) != command_result::OK)
if (set_pdp_context(pdp) != command_result::OK) {
return false;
}
return true;
}
bool set_mode(modem_mode mode) override
{
if (mode == modem_mode::DATA_MODE) {
if (set_data_mode() != command_result::OK)
switch (mode) {
case esp_modem::modem_mode::DATA_MODE:
if (set_data_mode() != command_result::OK) {
return resume_data_mode() == command_result::OK;
}
return true;
}
if (mode == modem_mode::COMMAND_MODE) {
case esp_modem::modem_mode::COMMAND_MODE:
return set_command_mode() == command_result::OK;
default:
return false;
}
return false;
}
bool init(const std::string& pin)
[[maybe_unused]] bool init_sim(const std::string &pin)
{
// switch to command mode (in case we were in PPP mode)
static_cast<void>(set_command_mode()); // ignore the potential failure, as we might be in command mode after startup
bool is_pin_ok;
if (read_pin(is_pin_ok) != command_result::OK)
if (read_pin(is_pin_ok) != command_result::OK) {
return false;
}
if (!is_pin_ok) {
if (set_pin(pin) != command_result::OK)
if (set_pin(pin) != command_result::OK) {
return false;
}
vTaskDelay(pdMS_TO_TICKS(1000));
if (read_pin(is_pin_ok) != command_result::OK || !is_pin_ok)
if (read_pin(is_pin_ok) != command_result::OK || !is_pin_ok) {
return false;
}
}
return true;
}
@ -109,68 +103,83 @@ private:
std::shared_ptr<DTE> dte;
std::string apn;
[[nodiscard]] command_result set_pdp_context(PdpContext& pdp) { return dce_commands::set_pdp_context(dte.get(),pdp); }
[[nodiscard]] command_result set_pin(const std::string &pin) { return dce_commands::set_pin(dte.get(), pin); }
[[nodiscard]] command_result read_pin(bool& pin_ok) { return dce_commands::read_pin(dte.get(), pin_ok); }
[[nodiscard]] command_result set_data_mode() { return dce_commands::set_data_mode(dte.get()); }
[[nodiscard]] command_result resume_data_mode() { return dce_commands::resume_data_mode(dte.get()); }
[[nodiscard]] command_result set_command_mode() { return dce_commands::set_command_mode(dte.get()); }
[[nodiscard]] command_result set_pdp_context(PdpContext &pdp)
{
return dce_commands::set_pdp_context(dte.get(), pdp);
}
[[nodiscard]] command_result set_pin(const std::string &pin)
{
return dce_commands::set_pin(dte.get(), pin);
}
[[nodiscard]] command_result read_pin(bool &pin_ok)
{
return dce_commands::read_pin(dte.get(), pin_ok);
}
[[nodiscard]] command_result set_data_mode()
{
return dce_commands::set_data_mode(dte.get());
}
[[nodiscard]] command_result resume_data_mode()
{
return dce_commands::resume_data_mode(dte.get());
}
[[nodiscard]] command_result set_command_mode()
{
return dce_commands::set_command_mode(dte.get());
}
};
esp_err_t PPPNetwork::init(esp_netif_t *netif, const std::string& apn, const std::string &pin_number)
/**
* @brief Implement the C-API for the AP-2-PPP functionality
*/
namespace {
/**
* @brief Local network object used to setup PPP network
*/
NetDCE *dce = nullptr;
extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
{
// configure
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.uart_config.rx_buffer_size = 16384;
dte_config.uart_config.tx_buffer_size = 2048;
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(apn.c_str());
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
// create DTE and minimal network DCE
auto uart_dte = create_uart_dte(&dte_config);
// create the specific device (and initialize it)
auto dev = NetDCE_Factory::create_module<NetModule>(&dce_config, uart_dte, netif);
if (!dev->init(pin_number))
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
if (!dev->init_sim(CONFIG_EXAMPLE_SIM_PIN)) {
return ESP_FAIL;
}
#endif
// now create the DCE from our already existent device
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif, dev);
if (dce == nullptr)
if (dce == nullptr) {
return ESP_FAIL;
}
return ESP_OK;
}
void PPPNetwork::deinit()
extern "C" void modem_start_network()
{
dce->set_mode(esp_modem::modem_mode::DATA_MODE);
}
extern "C" void modem_stop_network()
{
dce->set_mode(esp_modem::modem_mode::COMMAND_MODE);
}
extern "C" void modem_deinit_network()
{
free(dce);
dce = nullptr;
}
NetDCE *PPPNetwork::get_dce()
{
return dce;
}
/**
* @brief Implement the C-API for the AP-2-PPP functionality
*/
extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
{
return ppp_network.init(netif, CONFIG_EXAMPLE_MODEM_PPP_APN, CONFIG_EXAMPLE_SIM_PIN);
}
extern "C" void modem_start_network()
{
ppp_network.get_dce()->set_mode(esp_modem::modem_mode::DATA_MODE);
}
extern "C" void modem_stop_network()
{
ppp_network.get_dce()->set_mode(esp_modem::modem_mode::COMMAND_MODE);
}
extern "C" void modem_deinit_network()
{
ppp_network.deinit();
}
}

View File

@ -7,8 +7,7 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef _NETWORK_DCE_H_
#define _NETWORK_DCE_H_
#pragma once
#ifdef __cplusplus
extern "C" {
@ -43,5 +42,3 @@ void modem_stop_network();
#ifdef __cplusplus
}
#endif
#endif //_NETWORK_DCE_H_

View File

@ -15,22 +15,22 @@
using namespace esp_modem;
[[maybe_unused]] static const char *TAG = "linux_modem_main";
[[maybe_unused]] constexpr auto TAG = "linux_modem_main";
int main()
{
// init the DTE
esp_modem_dte_config_t dte_config = {
.dte_buffer_size = 512,
.task_stack_size = 1024,
.task_priority = 10,
.vfs_config = {}
.dte_buffer_size = 512,
.task_stack_size = 1024,
.task_priority = 10,
.vfs_config = {}
};
#if CONFIG_USE_VFS_UART == 1
struct esp_modem_vfs_uart_creator uart_config = {
.dev_name = "/dev/ttyUSB0",
.uart = {}
.dev_name = "/dev/ttyUSB0",
.uart = {}
};
assert(vfs_create_uart(&uart_config, &dte_config.vfs_config) == true);
#else
@ -39,16 +39,16 @@ int main()
* socat TCP-L:2222 GOPEN:/dev/ttyS0,ispeed=115200,ospeed=1152000,b115200,raw,echo=0
*/
struct esp_modem_vfs_socket_creator socket_config = {
.host_name = "raspberrypi.local",
.port = 2222
.host_name = "raspberrypi.local",
.port = 2222
};
assert(vfs_create_socket(&socket_config, &dte_config.vfs_config) == true);
#endif
auto dte = create_vfs_dte(&dte_config);
esp_netif_config_t netif_config = {
.dev_name = "/dev/net/tun",
.if_name = "tun0"
.dev_name = "/dev/net/tun",
.if_name = "tun0"
};
esp_netif_t *tun_netif = esp_netif_new(&netif_config);
@ -67,12 +67,13 @@ int main()
std::string str;
// dce->set_mode(esp_modem::modem_mode::CMUX_MODE);
dce->get_imsi(str);
ESP_LOGI(TAG, "Modem IMSI number: %s",str.c_str());
ESP_LOGI(TAG, "Modem IMSI number: %s", str.c_str());
dce->get_imei(str);
ESP_LOGI(TAG, "Modem IMEI number: %s",str.c_str());
while (command_result::OK != dce->get_operator_name(str))
{ printf(".\n"); }
ESP_LOGI(TAG, "Operator name: %s",str.c_str());
ESP_LOGI(TAG, "Modem IMEI number: %s", str.c_str());
while (command_result::OK != dce->get_operator_name(str)) {
printf(".\n");
}
ESP_LOGI(TAG, "Operator name: %s", str.c_str());
dce->set_mode(esp_modem::modem_mode::DATA_MODE);

View File

@ -1,4 +1,4 @@
# PPPoS simple client example
# Modem console example
(See the README.md file in the upper level 'examples' directory for more information about examples.)

View File

@ -12,50 +12,52 @@
static const char *TAG = "modem_console_helper";
ConsoleCommand::ConsoleCommand(const char* command, const char* help, const std::vector<CommandArgs>& args, std::function<bool(ConsoleCommand *)> f):
func(std::move(f))
ConsoleCommand::ConsoleCommand(const char *command, const char *help, const std::vector<CommandArgs> &args, std::function<bool(ConsoleCommand *)> f):
func(std::move(f))
{
RegisterCommand(command, help, args);
}
void ConsoleCommand::RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args)
void ConsoleCommand::RegisterCommand(const char *command, const char *help, const std::vector<CommandArgs> &args)
{
assert(last_command <= MAX_REPEAT_NR);
void * common_arg = nullptr;
for (auto it: args) {
switch(it.type) {
case ARG_END:
break;
case STR0:
common_arg = arg_str0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case STR1:
common_arg = arg_str1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT0:
common_arg = arg_int0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT1:
common_arg = arg_int1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case LIT0:
common_arg = arg_lit0(it.shortopts, it.longopts, it.glossary);
break;
arg_type common_arg = { };
for (auto &it : args) {
switch (it.type) {
case ARG_END:
break;
case STR0:
common_arg.str = arg_str0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case STR1:
common_arg.str = arg_str1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT0:
common_arg.intx = arg_int0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT1:
common_arg.intx = arg_int1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case LIT0:
common_arg.lit = arg_lit0(it.shortopts, it.longopts, it.glossary);
break;
}
if (common_arg) {
if (common_arg.is_null()) {
arg_table.emplace_back(common_arg);
} else {
ESP_LOGE(TAG, "Creating argument parser failed for %s", it.glossary);
abort();
}
}
arg_table.emplace_back( arg_end(1));
arg_type end = { .end = arg_end(1) };
arg_table.emplace_back(end);
const esp_console_cmd_t command_def = {
.command = command,
.help = help,
.hint = nullptr,
.func = command_func_pts[last_command],
.argtable = &arg_table[0]
.command = command,
.help = help,
.hint = nullptr,
.func = command_func_pts[last_command],
.argtable = &arg_table[0]
};
ESP_ERROR_CHECK(esp_console_cmd_register(&command_def));
last_command++;
@ -64,13 +66,13 @@ void ConsoleCommand::RegisterCommand(const char* command, const char* help, cons
int ConsoleCommand::get_count(int index)
{
return ((struct arg_str *)arg_table[index])->count;
return (arg_table[index].str)->count;
}
std::string ConsoleCommand::get_string(int index)
{
if (get_count(index) > 0) {
return std::string(((struct arg_str *)arg_table[index])->sval[0]);
return std::string(arg_table[index].str->sval[0]);
}
return std::string();
}
@ -78,17 +80,18 @@ std::string ConsoleCommand::get_string(int index)
int ConsoleCommand::get_int(int index)
{
if (get_count(index) > 0) {
return *((struct arg_int *)arg_table[index])->ival;
return *(arg_table[index].intx)->ival;
}
return -1;
}
int ConsoleCommand::command_func(int argc, char **argv) {
void * plain_arg_array = &arg_table[0];
int ConsoleCommand::command_func(int argc, char **argv)
{
arg_type *plain_arg_array = &arg_table[0];
int nerrors = arg_parse(argc, argv, (void **)plain_arg_array);
if (nerrors != 0) {
arg_print_errors(stderr, (struct arg_end *) arg_table.back(), argv[0]);
arg_print_errors(stderr, arg_table.back().end, argv[0]);
return 1;
}
if (func) {
@ -120,7 +123,7 @@ const esp_console_cmd_func_t ConsoleCommand::command_func_pts[] = {
#define ITEM_TO_REPEAT(index) StaticCommands::command_func_ ## index ,
_DO_REPEAT_ITEM()
_DO_REPEAT_ITEM()
#undef ITEM_TO_REPEAT
};
@ -128,5 +131,5 @@ const esp_console_cmd_func_t ConsoleCommand::command_func_pts[] = {
/**
* @brief Static members defined for ConsoleCommand
*/
std::vector<ConsoleCommand*> ConsoleCommand::console_commands;
std::vector<ConsoleCommand *> ConsoleCommand::console_commands;
int ConsoleCommand::last_command = 0;

View File

@ -7,8 +7,7 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef MODEM_CONSOLE_CONSOLE_HELPER_H
#define MODEM_CONSOLE_CONSOLE_HELPER_H
#pragma once
#include <vector>
#include <algorithm>
@ -35,10 +34,10 @@ enum arg_type {
* Command argument struct definition for list of arguments of one command
*/
struct CommandArgs {
CommandArgs(arg_type t, const char * shopts, const char * lopts, const char * data, const char * glos):
CommandArgs(arg_type t, const char *shopts, const char *lopts, const char *data, const char *glos):
type(t), shortopts(shopts), longopts(lopts), datatype(data), glossary(glos) {}
CommandArgs(arg_type t, const char * shopts, const char * lopts, const char * glos):
type(t), shortopts(shopts), longopts(lopts), datatype(nullptr), glossary(glos) {}
CommandArgs(arg_type t, const char *shopts, const char *lopts, const char *glos):
type(t), shortopts(shopts), longopts(lopts), datatype(nullptr), glossary(glos) {}
arg_type type;
const char *shortopts;
@ -53,6 +52,21 @@ class StaticCommands;
* @brief This class simplifies console command definition in more object wise fashion
*/
class ConsoleCommand {
/**
* @brief Common argument types to be stored internally for parsing later
*/
using arg_type =
union {
struct arg_int *intx;
struct arg_str *str;
struct arg_lit *lit;
struct arg_end *end;
void *__raw_ptr;
bool is_null() const {
return __raw_ptr;
}
};
friend class StaticCommands;
public:
/**
@ -63,8 +77,8 @@ public:
* @param srg_struct_size Size of the argument struct
* @param f Function callback for the command
*/
template<typename T> explicit ConsoleCommand(const char* command, const char* help, const T *arg_struct, size_t srg_struct_size,
std::function<bool(ConsoleCommand *)> f): func(std::move(f))
template<typename T> explicit ConsoleCommand(const char *command, const char *help, const T *arg_struct, size_t srg_struct_size,
std::function<bool(ConsoleCommand *)> f): func(std::move(f))
{
size_t args_plain_size = srg_struct_size / sizeof(CommandArgs);
auto first_arg = reinterpret_cast<const CommandArgs *>(arg_struct);
@ -75,29 +89,38 @@ public:
/**
* @brief Another method of Console command definitions using vector arg struct
*/
explicit ConsoleCommand(const char* command, const char* help, const std::vector<CommandArgs>& args, std::function<bool(ConsoleCommand *)> f);
explicit ConsoleCommand(const char *command, const char *help, const std::vector<CommandArgs> &args, std::function<bool(ConsoleCommand *)> f);
/**
* @brief Utility getters of various params from the argument list
*/
template<typename T> int get_count_of(CommandArgs T::*member) { return get_count(index_arg(member)); }
template<typename T> std::string get_string_of(CommandArgs T::*member) { return get_string(index_arg(member)); }
template<typename T> int get_int_of(CommandArgs T::*member) { return get_int(index_arg(member)); }
template<typename T> int get_count_of(CommandArgs T::*member)
{
return get_count(index_arg(member));
}
template<typename T> std::string get_string_of(CommandArgs T::*member)
{
return get_string(index_arg(member));
}
template<typename T> int get_int_of(CommandArgs T::*member)
{
return get_int(index_arg(member));
}
std::string get_string(int index);
int get_int(int index);
private:
int get_count(int index);
void RegisterCommand(const char* command, const char* help, const std::vector<CommandArgs>& args);
void RegisterCommand(const char *command, const char *help, const std::vector<CommandArgs> &args);
template<typename T> static constexpr size_t index_arg(CommandArgs T::*member)
{ return ((uint8_t *)&((T*)nullptr->*member) - (uint8_t *)nullptr)/sizeof(CommandArgs); }
std::vector<void*> arg_table;
{
return ((uint8_t *) & ((T *)nullptr->*member) - (uint8_t *)nullptr) / sizeof(CommandArgs);
}
std::vector<arg_type> arg_table;
int command_func(int argc, char **argv);
static int last_command;
static std::vector<ConsoleCommand*> console_commands;
static std::vector<ConsoleCommand *> console_commands;
std::function<bool(ConsoleCommand *)> func;
const static esp_console_cmd_func_t command_func_pts[];
};
#endif //MODEM_CONSOLE_CONSOLE_HELPER_H

View File

@ -46,6 +46,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
default: break;
}
return ESP_OK;
}
@ -83,9 +84,9 @@ static int do_http_client(int argc, char **argv)
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
uint64_t content_length = esp_http_client_get_content_length(client);
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %lld",
esp_http_client_get_status_code(client), content_length);
return 0;
}
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
@ -105,4 +106,4 @@ void modem_console_register_http(void)
.argtable = &http_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&http_cmd));
}
}

View File

@ -73,7 +73,7 @@ extern "C" void app_main(void)
SetModeArgs(): mode(STR1, nullptr, nullptr, "<mode>", "PPP, CMD or CMUX") {}
CommandArgs mode;
} set_mode_args;
const ConsoleCommand SetModeParser("set_mode", "sets modem mode", &set_mode_args, sizeof(set_mode_args), [&](ConsoleCommand *c){
const ConsoleCommand SetModeParser("set_mode", "sets modem mode", &set_mode_args, sizeof(set_mode_args), [&](ConsoleCommand * c) {
if (c->get_count_of(&SetModeArgs::mode)) {
auto mode = c->get_string_of(&SetModeArgs::mode);
modem_mode dev_mode;
@ -101,7 +101,7 @@ extern "C" void app_main(void)
SetPinArgs(): pin(STR1, nullptr, nullptr, "<pin>", "PIN") {}
CommandArgs pin;
} set_pin_args;
const ConsoleCommand SetPinParser("set_pin", "sets SIM card PIN", &set_pin_args, sizeof(set_pin_args), [&](ConsoleCommand *c){
const ConsoleCommand SetPinParser("set_pin", "sets SIM card PIN", &set_pin_args, sizeof(set_pin_args), [&](ConsoleCommand * c) {
if (c->get_count_of(&SetPinArgs::pin)) {
auto pin = c->get_string_of(&SetPinArgs::pin);
ESP_LOGI(TAG, "Setting pin=%s...", pin.c_str());
@ -109,7 +109,7 @@ extern "C" void app_main(void)
if (err == command_result::OK) {
ESP_LOGI(TAG, "OK");
} else {
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT":"");
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT" : "");
return 1;
}
}
@ -117,26 +117,26 @@ extern "C" void app_main(void)
});
const std::vector<CommandArgs> no_args;
const ConsoleCommand ReadPinArgs("read_pin", "checks if SIM is unlocked", no_args, [&](ConsoleCommand *c){
const ConsoleCommand ReadPinArgs("read_pin", "checks if SIM is unlocked", no_args, [&](ConsoleCommand * c) {
bool pin_ok;
ESP_LOGI(TAG, "Checking pin...");
auto err = dce->read_pin(pin_ok);
if (err == command_result::OK) {
ESP_LOGI(TAG, "OK. Pin status: %s", pin_ok ? "true": "false");
ESP_LOGI(TAG, "OK. Pin status: %s", pin_ok ? "true" : "false");
} else {
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT":"");
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT" : "");
return 1;
}
return 0;
});
const ConsoleCommand GetModuleName("get_module_name", "reads the module name", no_args, [&](ConsoleCommand *c){
const ConsoleCommand GetModuleName("get_module_name", "reads the module name", no_args, [&](ConsoleCommand * c) {
std::string module_name;
ESP_LOGI(TAG, "Reading module name...");
CHECK_ERR(dce->get_module_name(module_name), ESP_LOGI(TAG, "OK. Module name: %s", module_name.c_str()));
});
const ConsoleCommand GetOperatorName("get_operator_name", "reads the operator name", no_args, [&](ConsoleCommand *c){
const ConsoleCommand GetOperatorName("get_operator_name", "reads the operator name", no_args, [&](ConsoleCommand * c) {
std::string operator_name;
ESP_LOGI(TAG, "Reading operator name...");
CHECK_ERR(dce->get_operator_name(operator_name), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
@ -153,39 +153,41 @@ extern "C" void app_main(void)
CommandArgs pattern;
CommandArgs no_cr;
} send_cmd_args;
const ConsoleCommand SendCommand("cmd", "sends generic AT command, no_args", &send_cmd_args, sizeof(send_cmd_args), [&](ConsoleCommand *c) {
const ConsoleCommand SendCommand("cmd", "sends generic AT command, no_args", &send_cmd_args, sizeof(send_cmd_args), [&](ConsoleCommand * c) {
auto cmd = c->get_string_of(&GenericCommandArgs::cmd);
auto timeout = c->get_count_of(&GenericCommandArgs::timeout) ? c->get_int_of(&GenericCommandArgs::timeout)
: 1000;
: 1000;
ESP_LOGI(TAG, "Sending command %s with timeout %d", cmd.c_str(), timeout);
auto pattern = c->get_string_of(&GenericCommandArgs::pattern);
if (c->get_count_of(&GenericCommandArgs::no_cr) == 0) {
cmd += '\r';
}
ESP_LOGI(TAG, "Sending command %s with timeout %d", cmd.c_str(), timeout);
CHECK_ERR(dce->command(cmd, [&](uint8_t *data, size_t len) {
std::string response((char *) data, len);
ESP_LOGI(TAG, "%s", response.c_str());
if (pattern.empty() || response.find(pattern) != std::string::npos)
if (pattern.empty() || response.find(pattern) != std::string::npos) {
return command_result::OK;
if (response.find(pattern) != std::string::npos)
}
if (response.find(pattern) != std::string::npos) {
return command_result::OK;
}
return command_result::TIMEOUT;
}, timeout),);
});
const ConsoleCommand GetSignalQuality("get_signal_quality", "Gets signal quality", no_args, [&](ConsoleCommand *c){
const ConsoleCommand GetSignalQuality("get_signal_quality", "Gets signal quality", no_args, [&](ConsoleCommand * c) {
int rssi, ber;
CHECK_ERR(dce->get_signal_quality(rssi, ber), ESP_LOGI(TAG, "OK. rssi=%d, ber=%d", rssi, ber));
});
const ConsoleCommand GetBatteryStatus("get_battery_status", "Reads voltage/battery status", no_args, [&](ConsoleCommand *c){
const ConsoleCommand GetBatteryStatus("get_battery_status", "Reads voltage/battery status", no_args, [&](ConsoleCommand * c) {
int volt, bcl, bcs;
CHECK_ERR(dce->get_battery_status(volt, bcl, bcs), ESP_LOGI(TAG, "OK. volt=%d, bcl=%d, bcs=%d", volt, bcl, bcs));
});
const ConsoleCommand PowerDown("power_down", "power down the module", no_args, [&](ConsoleCommand *c){
const ConsoleCommand PowerDown("power_down", "power down the module", no_args, [&](ConsoleCommand * c) {
ESP_LOGI(TAG, "Power down the module...");
CHECK_ERR(dce->power_down(), ESP_LOGI(TAG, "OK"));
});
const ConsoleCommand Reset("reset", "reset the module", no_args, [&](ConsoleCommand *c){
const ConsoleCommand Reset("reset", "reset the module", no_args, [&](ConsoleCommand * c) {
ESP_LOGI(TAG, "Resetting the module...");
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
});
@ -193,7 +195,7 @@ extern "C" void app_main(void)
SetApn(): apn(STR1, nullptr, nullptr, "<apn>", "APN (Access Point Name)") {}
CommandArgs apn;
} set_apn;
const ConsoleCommand SetApnParser("set_apn", "sets APN", &set_apn, sizeof(set_apn), [&](ConsoleCommand *c){
const ConsoleCommand SetApnParser("set_apn", "sets APN", &set_apn, sizeof(set_apn), [&](ConsoleCommand * c) {
if (c->get_count_of(&SetApn::apn)) {
auto apn = c->get_string_of(&SetApn::apn);
ESP_LOGI(TAG, "Setting the APN=%s...", apn.c_str());
@ -205,7 +207,7 @@ extern "C" void app_main(void)
});
SignalGroup exit_signal;
const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand *c){
const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand * c) {
ESP_LOGI(TAG, "Exiting...");
exit_signal.set(1);
s_repl->del(s_repl);

View File

@ -7,8 +7,7 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef __MY_MODULE_DCE_HPP__
#define __MY_MODULE_DCE_HPP__
#pragma once
#include "cxx_include/esp_modem_dce_factory.hpp"
@ -21,7 +20,7 @@
class MyShinyModem: public esp_modem::GenericModule {
using GenericModule::GenericModule;
public:
esp_modem::command_result get_module_name(std::string& name) override
esp_modem::command_result get_module_name(std::string &name) override
{
name = "Custom Shiny Module";
return esp_modem::command_result::OK;
@ -33,11 +32,8 @@ public:
* @return unique pointer of the resultant DCE
*/
std::unique_ptr<esp_modem::DCE> create_shiny_dce(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return esp_modem::dce_factory::Factory::build_unique<MyShinyModem>(config, std::move(dte), netif);
}
#endif //__MY_MODULE_DCE_HPP__

View File

@ -122,10 +122,10 @@ extern "C" void app_main(void)
event_handler.listen_to(MqttClient::get_event(MqttClient::Event::DATA));
auto reg = loop->register_event(MqttClient::get_event(MqttClient::Event::DATA),
[&mqtt](const ESPEvent &event, void *data) {
std::cout << " TOPIC:" << mqtt.get_topic(data) << std::endl;
std::cout << " DATA:" << mqtt.get_data(data) << std::endl;
});
[&mqtt](const ESPEvent & event, void *data) {
std::cout << " TOPIC:" << mqtt.get_topic(data) << std::endl;
std::cout << " DATA:" << mqtt.get_data(data) << std::endl;
});
mqtt.connect();
while (true) {
result = event_handler.wait_event_for(std::chrono::milliseconds(60000));

View File

@ -22,9 +22,8 @@ ESP_EVENT_DECLARE_BASE(MQTT_EVENTS);
/**
* Thin wrapper around C mqtt_client
*/
struct MqttClientHandle
{
explicit MqttClientHandle(const std::string & uri)
struct MqttClientHandle {
explicit MqttClientHandle(const std::string &uri)
{
esp_mqtt_client_config_t config = { };
config.uri = uri.c_str();
@ -49,9 +48,9 @@ struct MqttClientHandle
/**
* @brief Definitions of MqttClient methods
*/
MqttClient::MqttClient(const std::string & uri):
MqttClient::MqttClient(const std::string &uri):
h(std::unique_ptr<MqttClientHandle>(new MqttClientHandle(uri)))
{}
{}
void MqttClient::connect()
{
@ -61,11 +60,11 @@ void MqttClient::connect()
idf::event::ESPEvent MqttClient::get_event(MqttClient::Event ev)
{
switch (ev) {
case Event::CONNECT: {
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_CONNECTED) };
}
case Event::DATA:
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_DATA) };
case Event::CONNECT: {
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_CONNECTED) };
}
case Event::DATA:
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_DATA) };
}
return { };
}
@ -80,7 +79,7 @@ int MqttClient::subscribe(const std::string &topic, int qos)
return esp_mqtt_client_subscribe(h->client, topic.c_str(), qos);
}
std::string MqttClient::get_topic(void * event_data)
std::string MqttClient::get_topic(void *event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)
@ -89,7 +88,7 @@ std::string MqttClient::get_topic(void * event_data)
return std::string(event->topic, event->topic_len);
}
std::string MqttClient::get_data(void * event_data)
std::string MqttClient::get_data(void *event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)

View File

@ -6,8 +6,7 @@
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef _SIMPLE_MQTT_CLIENT_H_
#define _SIMPLE_MQTT_CLIENT_H_
#pragma once
#include <string>
#include <memory>
@ -25,7 +24,7 @@ public:
DATA,
};
explicit MqttClient(const std::string & uri);
explicit MqttClient(const std::string &uri);
~MqttClient();
/**
@ -40,7 +39,7 @@ public:
* @param qos QoS (0 by default)
* @return message id
*/
int publish(const std::string & topic, const std::string & data, int qos = 0);
int publish(const std::string &topic, const std::string &data, int qos = 0);
/**
* @brief Subscribe to a topic
@ -48,7 +47,7 @@ public:
* @param qos QoS (0 by default)
* @return message id
*/
int subscribe(const std::string & topic, int qos = 0);
int subscribe(const std::string &topic, int qos = 0);
/**
* @brief Get topic from event data
@ -72,6 +71,3 @@ public:
private:
std::unique_ptr<MqttClientHandle> h;
};
#endif //_SIMPLE_MQTT_CLIENT_H_

View File

@ -6,5 +6,7 @@ CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_PPP_ENABLE_IPV6=n
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_PARTITION_TABLE_TWO_OTA=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF=y
CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=y
CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=y
CONFIG_MAIN_TASK_STACK_SIZE=8192

View File

@ -1,4 +1,4 @@
version: "0.1.7"
version: "0.1.13"
description: esp modem
dependencies:
# Required IDF version

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_API_HPP_
#define _ESP_MODEM_API_HPP_
#pragma once
#include <memory>
#include "cxx_include/esp_modem_dce.hpp"
@ -74,7 +73,7 @@ std::shared_ptr<DTE> create_vfs_dte(const dte_config *config);
/**
* @brief Create DCE based on SIM7600 module
* @param config DCE configuration
* @param DTE reference to the communicating DTE
* @param dte reference to the communicating DTE
* @param netif reference to the network interface
*
* @return unique ptr to the created DCE on success
@ -103,5 +102,3 @@ std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_pt
*/
} // namespace esp_modem
#endif // _ESP_MODEM_API_HPP_

View File

@ -12,14 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_CMUX_HPP_
#define _ESP_MODEM_CMUX_HPP_
#pragma once
#include "esp_modem_terminal.hpp"
namespace esp_modem {
constexpr size_t max_terms = 2;
constexpr size_t MAX_TERMINALS_NUM = 2;
/**
* @defgroup ESP_MODEM_CMUX ESP_MODEM CMUX class
* @brief Definition of CMUX terminal
@ -51,38 +50,79 @@ class CMuxInstance;
/**
* @brief CMux class which consumes the original terminal and creates multiple virtual terminals from it.
* This class is not usable applicable as a DTE terminal
* This class itself is not usable as a DTE terminal, only via its instances defined in `CMuxInstance`
*/
class CMux {
public:
explicit CMux(std::unique_ptr<Terminal> t, std::unique_ptr<uint8_t[]> b, size_t buff_size):
term(std::move(t)), buffer_size(buff_size), buffer(std::move(b)), payload_start(nullptr), total_payload_size(0) {}
term(std::move(t)), payload_start(nullptr), total_payload_size(0), buffer_size(buff_size), buffer(std::move(b)) {}
~CMux() = default;
/**
* @brief Initializes CMux protocol
* @return true on success
*/
[[nodiscard]] bool init();
/**
* @brief Sets read callback for the appropriate terminal
* @param inst Index of the terminal
* @param f function pointer
*/
void set_read_cb(int inst, std::function<bool(uint8_t *data, size_t len)> f);
/**
* @brief Writes to the appropriate terminal
* @param i Index of the terminal
* @param data Data to write
* @param len Data length to write
* @return The actual written length
*/
int write(int i, uint8_t *data, size_t len);
private:
std::function<bool(uint8_t *data, size_t len)> read_cb[max_terms];
void data_available(uint8_t *data, size_t len);
void send_sabm(size_t i);
std::unique_ptr<Terminal> term;
cmux_state state;
static uint8_t fcs_crc(const uint8_t frame[6]); /*!< Utility to calculate FCS CRC */
void data_available(uint8_t *data, size_t len); /*!< Called when valid data available */
void send_sabm(size_t i); /*!< Sending initial SABM */
bool on_cmux(uint8_t *data, size_t len); /*!< Called from terminal layer when raw CMUX protocol data available */
struct CMuxFrame; /*!< Forward declare the Frame struct, used in protocol decoders */
/**
* These methods serve different states of the CMUX protocols
* @param frame Currently available cmux frame (basically data, size, methods)
* @return - true if the state processed successfully
* - false if more data needed to process the current state
*/
bool on_recovery(CMuxFrame &frame);
bool on_init(CMuxFrame &frame);
bool on_header(CMuxFrame &frame);
bool on_payload(CMuxFrame &frame);
bool on_footer(CMuxFrame &frame);
std::function<bool(uint8_t *data, size_t len)> read_cb[MAX_TERMINALS_NUM]; /*!< Function pointers to read callbacks */
std::unique_ptr<Terminal> term; /*!< The original terminal */
cmux_state state; /*!< CMux protocol state */
/**
* CMux control fields and offsets
*/
uint8_t dlci;
uint8_t type;
size_t payload_len;
uint8_t frame_header[6];
size_t frame_header_offset;
size_t buffer_size;
std::unique_ptr<uint8_t[]> buffer;
bool on_cmux(uint8_t *data, size_t len);
static uint8_t fcs_crc(const uint8_t frame[6]);
Lock lock;
int instance;
int sabm_ack;
uint8_t *payload_start;
size_t total_payload_size;
int instance;
int sabm_ack;
/**
* Processing buffer size and pointer
*/
size_t buffer_size;
std::unique_ptr<uint8_t[]> buffer;
Lock lock;
};
/**
@ -93,14 +133,23 @@ class CMuxInstance: public Terminal {
public:
explicit CMuxInstance(std::shared_ptr<CMux> parent, int i): cmux(std::move(parent)), instance(i) {}
int write(uint8_t *data, size_t len) override { return cmux->write(instance, data, len); }
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override { return cmux->set_read_cb(instance, std::move(f)); }
int read(uint8_t *data, size_t len) override { return 0; }
int write(uint8_t *data, size_t len) override
{
return cmux->write(instance, data, len);
}
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
{
return cmux->set_read_cb(instance, std::move(f));
}
int read(uint8_t *data, size_t len) override
{
return 0;
}
void start() override { }
void stop() override { }
private:
std::shared_ptr<CMux> cmux;
int instance;
size_t instance;
};
/**
@ -108,5 +157,3 @@ private:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_CMUX_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_COMMAND_LIBRARY_HPP_
#define _ESP_MODEM_COMMAND_LIBRARY_HPP_
#pragma once
#include "esp_modem_dte.hpp"
#include "esp_modem_dce_module.hpp"
@ -37,17 +36,17 @@ namespace dce_commands {
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
return_type name(CommandableIf *t, ## __VA_ARGS__);
DECLARE_ALL_COMMAND_APIS(declare name(Commandable *p, ...);)
DECLARE_ALL_COMMAND_APIS(declare name(Commandable *p, ...);)
#undef ESP_MODEM_DECLARE_DCE_COMMAND
/**
* @brief Following commands that are different for some specific modules
*/
command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl);
command_result power_down_sim7xxx(CommandableIf* t);
command_result power_down_sim8xx(CommandableIf* t);
command_result set_data_mode_sim8xx(CommandableIf* t);
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl);
command_result power_down_sim7xxx(CommandableIf *t);
command_result power_down_sim8xx(CommandableIf *t);
command_result set_data_mode_sim8xx(CommandableIf *t);
/**
* @}
@ -55,5 +54,3 @@ command_result set_data_mode_sim8xx(CommandableIf* t);
} // dce_commands
} // esp_modem
#endif //_ESP_MODEM_COMMAND_LIBRARY_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_DCE_HPP_
#define _ESP_MODEM_DCE_HPP_
#pragma once
#include <utility>
#include "cxx_include/esp_modem_netif.hpp"
@ -53,8 +52,8 @@ template<class SpecificModule>
class DCE_T {
static_assert(std::is_base_of<ModuleIf, SpecificModule>::value, "DCE must be instantiated with Module class only");
public:
explicit DCE_T(const std::shared_ptr<DTE>& dte, std::shared_ptr<SpecificModule> dev, esp_netif_t * netif):
dte(dte), device(std::move(dev)), netif(dte, netif)
explicit DCE_T(const std::shared_ptr<DTE> &dte, std::shared_ptr<SpecificModule> dev, esp_netif_t *netif):
dte(dte), device(std::move(dev)), netif(dte, netif)
{ }
~DCE_T() = default;
@ -62,20 +61,35 @@ public:
/**
* @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);
}
void set_cmux() { set_mode(modem_mode::CMUX_MODE); }
void set_cmux()
{
set_mode(modem_mode::CMUX_MODE);
}
SpecificModule* get_module() { return device.get(); }
SpecificModule *get_module()
{
return device.get();
}
command_result command(const std::string& command, got_line_cb got_line, uint32_t time_ms)
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms)
{
return dte->command(command, std::move(got_line), time_ms);
}
bool set_mode(modem_mode m) { return mode.set(dte.get(), device.get(), netif, m); }
bool set_mode(modem_mode m)
{
return mode.set(dte.get(), device.get(), netif, m);
}
protected:
std::shared_ptr<DTE> dte;
@ -86,9 +100,9 @@ protected:
/**
* @brief Common abstraction of the modem DCE, specialized by the GenericModule which is a parent class for the supported
* defices and most common modems, as well.
* devices and most common modems, as well.
*/
class DCE: public DCE_T<GenericModule> {
class DCE : public DCE_T<GenericModule> {
public:
using DCE_T<GenericModule>::DCE_T;
@ -99,7 +113,10 @@ public:
return device->name(std::forward<Agrs>(args)...); \
}
DECLARE_ALL_COMMAND_APIS(forwards name(...) { device->name(...); } )
DECLARE_ALL_COMMAND_APIS(forwards name(...)
{
device->name(...);
} )
#undef ESP_MODEM_DECLARE_DCE_COMMAND
@ -110,5 +127,3 @@ public:
*/
} // esp_modem
#endif // _ESP_MODEM_DCE_HPP_

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_DCE_FACTORY_HPP_
#define _ESP_MODEM_DCE_FACTORY_HPP_
#pragma once
#include "esp_log.h"
/**
* @defgroup ESP_MODEM_DCE_FACTORY
@ -31,83 +31,90 @@ using config = ::esp_modem_dce_config;
/**
* @brief Helper class for creating a uder define pointer in a specific way, either as a plain pointer, shared_ptr or unique_ptr
* @brief Helper class for creating a user define pointer in a specific way, either as a plain pointer, shared_ptr or unique_ptr
*/
class FactoryHelper {
public:
static std::unique_ptr<PdpContext> create_pdp_context(std::string &apn);
template <typename T, typename Ptr, typename ...Args>
static auto make(Args&&... args) -> typename std::enable_if<std::is_same<Ptr, T*>::value, T*>::type
template <typename T, typename T_Ptr, typename ...Args>
static auto make(Args &&... args) -> typename std::enable_if<std::is_same<T_Ptr, T *>::value, T *>::type
{
return new T(std::forward<Args>(args)...);
}
template <typename T, typename Ptr, typename ...Args>
static auto make(Args&&... args) -> typename std::enable_if<std::is_same<Ptr, std::shared_ptr<T>>::value, std::shared_ptr<T>>::type
template <typename T, typename T_Ptr, typename ...Args>
static auto make(Args &&... args) -> typename std::enable_if<std::is_same<T_Ptr, std::shared_ptr<T>>::value, std::shared_ptr<T>>::type
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
template <typename T, typename Ptr = std::unique_ptr<T>, typename ...Args>
static auto make(Args&&... args) -> typename std::enable_if<std::is_same<Ptr, std::unique_ptr<T>>::value, std::unique_ptr<T>>::type
template <typename T, typename T_Ptr = std::unique_ptr<T>, typename ...Args>
static auto make(Args && ... args) -> typename std::enable_if<std::is_same<T_Ptr, std::unique_ptr<T>>::value, std::unique_ptr<T>>::type
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
};
/**
* @brief Builder class for building a DCE_T<Module> in a specific way, either form a Module object or by default from the DTE and netif
* @brief Creator class for building a DCE_T<Module> in a specific way, either from a Module object or by default from the DTE and netif
*
* @throws
* - esp_modem::esp_err_exception on invalid arguments
* - std::bad_alloc if failed to allocate
*/
template<typename Module>
class Builder {
static_assert(std::is_base_of<ModuleIf, Module>::value, "Builder must be used only for Module classes");
template<typename T_Module>
class Creator {
static_assert(std::is_base_of<ModuleIf, T_Module>::value, "Builder must be used only for Module classes");
public:
Builder(std::shared_ptr<DTE> x, esp_netif_t* esp_netif): dte(std::move(x)), device(nullptr), netif(esp_netif)
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif): dte(std::move(dte)), device(nullptr), netif(esp_netif)
{
throw_if_false(netif != nullptr, "Null netif");
}
Builder(std::shared_ptr<DTE> dte, esp_netif_t* esp_netif, std::shared_ptr<Module> dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif)
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif, std::shared_ptr<T_Module> dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif)
{
throw_if_false(netif != nullptr, "Null netif");
}
~Builder()
~Creator()
{
throw_if_false(device == nullptr, "module was captured or created but never used");
if (device != nullptr) {
ESP_LOGE("dce_factory::~Creator", "module was captured or created but never used");
}
}
template<typename Ptr>
auto create_module(const esp_modem_dce_config *config) -> Ptr
template<typename T_Ptr>
auto create_module(const esp_modem_dce_config *config) -> T_Ptr
{
return FactoryHelper::make<Module, Ptr>(dte, config);
return FactoryHelper::make<T_Module, T_Ptr>(dte, config);
}
template<typename DceT, typename Ptr>
auto create(const esp_modem_dce_config *config) -> Ptr
template<typename T_Dce, typename T_Ptr>
auto create(const esp_modem_dce_config *config) -> T_Ptr
{
if (dte == nullptr)
if (dte == nullptr) {
return nullptr;
}
if (device == nullptr) {
device = create_module<decltype(device)>(config);
if (device == nullptr)
if (device == nullptr) {
return nullptr;
}
}
return FactoryHelper::make<DceT, Ptr>(std::move(dte), std::move(device), netif);
return FactoryHelper::make<T_Dce, T_Ptr>(std::move(dte), std::move(device), netif);
}
private:
std::shared_ptr<DTE> dte;
std::shared_ptr<Module> device;
std::shared_ptr<T_Module> device;
esp_netif_t *netif;
};
/**
* @brief Specific modem choice when creating by the Factory
*/
enum class Modem {
enum class ModemType {
GenericModule, /*!< Default generic module with the most common commands */
SIM7600, /*!< Derived from the GenericModule, specifics applied to SIM7600 model */
BG96, /*!< Derived from the GenericModule, specifics applied to BG69 model */
@ -120,7 +127,7 @@ enum class Modem {
*/
class Factory {
public:
explicit Factory(Modem modem): m(modem) {}
explicit Factory(ModemType modem): m(modem) {}
/**
* @brief Create a default unique_ptr DCE in a specific way (from the module)
@ -130,10 +137,10 @@ public:
* @param args typically a DTE object and a netif handle for PPP network
* @return unique_ptr DCE of the created DCE on success
*/
template <typename Module, typename ...Args>
static std::unique_ptr<DCE> build_unique(const config *cfg, Args&&... args)
template <typename T_Module, typename ...Args>
static std::unique_ptr<DCE> build_unique(const config *cfg, Args &&... args)
{
return build_generic_DCE<Module, DCE, std::unique_ptr<DCE>>(cfg, std::forward<Args>(args)...);
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(cfg, std::forward<Args>(args)...);
}
/**
@ -144,34 +151,34 @@ public:
* @param args typically a DTE object and a netif handle for PPP network
* @return DCE pointer the created DCE on success
*/
template <typename Module, typename ...Args>
static DCE* build(const config *cfg, Args&&... args)
template <typename T_Module, typename ...Args>
static DCE *build(const config *cfg, Args &&... args)
{
return build_generic_DCE<Module, DCE>(cfg, std::forward<Args>(args)...);
return build_generic_DCE<T_Module, DCE>(cfg, std::forward<Args>(args)...);
}
template <typename Module, typename ...Args>
static std::shared_ptr<Module> build_shared_module(const config *cfg, Args&&... args)
template <typename T_Module, typename ...Args>
static std::shared_ptr<T_Module> build_shared_module(const config *cfg, Args &&... args)
{
return build_module_T<Module>(cfg, std::forward<Args>(args)...);
return build_module_T<T_Module>(cfg, std::forward<Args>(args)...);
}
template <typename ...Args>
std::shared_ptr<GenericModule> build_shared_module(const config *cfg, Args&&... args)
std::shared_ptr<GenericModule> build_shared_module(const config *cfg, Args &&... args)
{
switch (m) {
case Modem::SIM800:
return build_shared_module<SIM800>(cfg, std::forward<Args>(args)...);
case Modem::SIM7600:
return build_shared_module<SIM7600>(cfg, std::forward<Args>(args)...);
case Modem::BG96:
return build_shared_module<BG96>(cfg, std::forward<Args>(args)...);
case Modem::GenericModule:
return build_shared_module<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
case ModemType::SIM800:
return build_shared_module<SIM800>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7600:
return build_shared_module<SIM7600>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_shared_module<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build_shared_module<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
}
return nullptr;
}
@ -184,58 +191,58 @@ public:
* @return unique_ptr DCE of the created DCE on success
*/
template <typename ...Args>
std::unique_ptr<DCE> build_unique(const config *cfg, Args&&... args)
std::unique_ptr<DCE> build_unique(const config *cfg, Args &&... args)
{
switch (m) {
case Modem::SIM800:
return build_unique<SIM800>(cfg, std::forward<Args>(args)...);
case Modem::SIM7600:
return build_unique<SIM7600>(cfg, std::forward<Args>(args)...);
case Modem::BG96:
return build_unique<BG96>(cfg, std::forward<Args>(args)...);
case Modem::GenericModule:
return build_unique<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
case ModemType::SIM800:
return build_unique<SIM800>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7600:
return build_unique<SIM7600>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_unique<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build_unique<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
}
return nullptr;
}
template <typename ...Args>
DCE* build(const config *cfg, Args&&... args)
DCE *build(const config *cfg, Args &&... args)
{
switch (m) {
case Modem::SIM800:
return build<SIM800>(cfg, std::forward<Args>(args)...);
case Modem::SIM7600:
return build<SIM7600>(cfg, std::forward<Args>(args)...);
case Modem::BG96:
return build<BG96>(cfg, std::forward<Args>(args)...);
case Modem::GenericModule:
return build<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
case ModemType::SIM800:
return build<SIM800>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7600:
return build<SIM7600>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build<GenericModule>(cfg, std::forward<Args>(args)...);
default:
break;
}
return nullptr;
}
private:
Modem m;
ModemType m;
protected:
template <typename Module, typename Ptr = std::shared_ptr<Module>, typename ...Args>
static Ptr build_module_T(const config *cfg, Args&&... args)
template <typename T_Module, typename Ptr = std::shared_ptr<T_Module>, typename ...Args>
static Ptr build_module_T(const config *cfg, Args && ... args)
{
Builder<Module> b(std::forward<Args>(args)...);
return b.template create_module<Ptr>(cfg);
Creator<T_Module> creator(std::forward<Args>(args)...);
return creator.template create_module<Ptr>(cfg);
}
template <typename Module, typename Dce = DCE_T<Module>, typename DcePtr = Dce*, typename ...Args>
static DcePtr build_generic_DCE(const config *cfg, Args&&... args)
template <typename T_Module, typename T_Dce = DCE_T<T_Module>, typename T_DcePtr = T_Dce *, typename ...Args>
static auto build_generic_DCE(const config *cfg, Args && ... args) -> T_DcePtr
{
Builder<Module> b(std::forward<Args>(args)...);
return b.template create<Dce, DcePtr>(cfg);
Creator<T_Module> creator(std::forward<Args>(args)...);
return creator.template create<T_Dce, T_DcePtr>(cfg);
}
};
@ -244,6 +251,3 @@ protected:
*/
} // namespace esp_modem::dce_factory
#endif // _ESP_MODEM_DCE_FACTORY_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_DCE_MODULE_
#define _ESP_MODEM_DCE_MODULE_
#pragma once
#include <memory>
#include <utility>
@ -48,8 +47,8 @@ public:
* The configuration could be either the dce-config struct or just a pdp context
*/
explicit GenericModule(std::shared_ptr<DTE> dte, std::unique_ptr<PdpContext> pdp):
dte(std::move(dte)), pdp(std::move(pdp)) {}
explicit GenericModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config* config);
dte(std::move(dte)), pdp(std::move(pdp)) {}
explicit GenericModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *config);
/**
* @brief This is a mandatory method for ModuleIf class, which sets up the device
@ -59,10 +58,12 @@ public:
*/
bool setup_data_mode() override
{
if (set_echo(false) != command_result::OK)
if (set_echo(false) != command_result::OK) {
return false;
if (set_pdp_context(*pdp) != command_result::OK)
}
if (set_pdp_context(*pdp) != command_result::OK) {
return false;
}
return true;
}
@ -73,10 +74,12 @@ public:
bool set_mode(modem_mode mode) override
{
if (mode == modem_mode::DATA_MODE) {
if (set_data_mode() != command_result::OK)
if (set_data_mode() != command_result::OK) {
return resume_data_mode() == command_result::OK;
}
return true;
} else if (mode == modem_mode::COMMAND_MODE) {
Task::Delay(1000); // Mandatory 1s pause
return set_command_mode() == command_result::OK;
} else if (mode == modem_mode::CMUX_MODE) {
return set_cmux() == command_result::OK;
@ -116,8 +119,7 @@ protected:
class SIM7600: public GenericModule {
using GenericModule::GenericModule;
public:
command_result get_module_name(std::string& name) override;
command_result get_battery_status(int& voltage, int &bcs, int &bcl) override;
command_result get_battery_status(int &voltage, int &bcs, int &bcl) override;
command_result power_down() override;
};
@ -127,7 +129,6 @@ public:
class SIM800: public GenericModule {
using GenericModule::GenericModule;
public:
command_result get_module_name(std::string& name) override;
command_result power_down() override;
command_result set_data_mode() override;
};
@ -137,8 +138,6 @@ public:
*/
class BG96: public GenericModule {
using GenericModule::GenericModule;
public:
command_result get_module_name(std::string& name) override;
};
/**
@ -146,5 +145,3 @@ public:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_DCE_MODULE_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_DTE_HPP_
#define _ESP_MODEM_DTE_HPP_
#pragma once
#include <memory>
#include <cstddef>
@ -117,5 +116,3 @@ private:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_DTE_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_EXCEPTION_HPP_
#define _ESP_MODEM_EXCEPTION_HPP_
#pragma once
#include <string>
#include "esp_err.h"
@ -27,9 +26,13 @@ public:
explicit esp_err_exception(esp_err_t err): esp_err(err) {}
explicit esp_err_exception(std::string msg): esp_err(ESP_FAIL), message(std::move(msg)) {}
explicit esp_err_exception(std::string msg, esp_err_t err): esp_err(err), message(std::move(msg)) {}
virtual esp_err_t get_err_t() { return esp_err; }
virtual esp_err_t get_err_t()
{
return esp_err;
}
~esp_err_exception() noexcept override = default;
virtual const char* what() const noexcept {
virtual const char *what() const noexcept
{
return message.c_str();
}
private:
@ -62,5 +65,3 @@ static inline void throw_if_esp_fail(esp_err_t err)
}
} // namespace esp_modem
#endif //_ESP_MODEM_EXCEPTION_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_NETIF_HPP
#define _ESP_MODEM_NETIF_HPP
#pragma once
#include <memory>
#include <cstddef>
@ -74,7 +73,7 @@ private:
std::shared_ptr<DTE> ppp_dte;
esp_netif_t *netif;
struct ppp_netif_driver driver{};
struct ppp_netif_driver driver {};
SignalGroup signal;
static const size_t PPP_STARTED = SignalGroup::bit0;
static const size_t PPP_EXIT = SignalGroup::bit1;
@ -85,5 +84,3 @@ private:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_NETIF_HPP

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_PRIMITIVES_HPP_
#define _ESP_MODEM_PRIMITIVES_HPP_
#pragma once
#include "esp_event.h"
#include "esp_modem_exception.hpp"
@ -23,9 +22,9 @@
#include <thread>
#else
#include "freertos/event_groups.h"
// forward declarations of FreeRTOS primitives
struct QueueDefinition;
typedef void * EventGroupHandle_t;
#endif
@ -33,7 +32,7 @@ namespace esp_modem {
// Forward declaration for both linux/FreeRTOS targets
//
using TaskFunction_t = void (*)(void*);
using TaskFunction_t = void (*)(void *);
#if !defined(CONFIG_IDF_TARGET_LINUX)
struct Lock {
using MutexT = QueueHandle_t;
@ -44,8 +43,8 @@ struct Lock {
private:
MutexT m{};
};
using TaskT = void*;
using SignalT = void*;
using TaskT = TaskHandle_t;
using SignalT = EventGroupHandle_t;
#else
using Lock = std::mutex;
struct SignalGroupInternal;
@ -57,11 +56,17 @@ static constexpr uint32_t portMAX_DELAY = UINT32_MAX;
template<class T>
class Scoped {
public:
explicit Scoped(T &l):lock(l) { lock.lock(); }
~Scoped() { lock.unlock(); }
explicit Scoped(T &l): lock(l)
{
lock.lock();
}
~Scoped()
{
lock.unlock();
}
private:
T& lock;
T &lock;
};
class Task {
@ -71,6 +76,7 @@ public:
static void Delete();
static void Relinquish();
static void Delay(uint32_t delay);
private:
TaskT task_handle;
};
@ -104,5 +110,3 @@ private:
};
} // namespace esp_modem
#endif // _ESP_MODEM_PRIMITIVES_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_TERMINAL_HPP_
#define _ESP_MODEM_TERMINAL_HPP_
#pragma once
#include <memory>
#include <functional>
@ -45,15 +44,21 @@ enum class terminal_error {
};
/**
* @brief Terminal interface. All communication interfaces must comply this interface in order to be used as a DTE
* @brief Terminal interface. All communication interfaces must comply to this interface in order to be used as a DTE
*/
class Terminal {
public:
virtual ~Terminal() = default;
void set_error_cb(std::function<void(terminal_error)> f) { on_error = std::move(f); }
void set_error_cb(std::function<void(terminal_error)> f)
{
on_error = std::move(f);
}
virtual void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) { on_data = std::move(f); }
virtual void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f)
{
on_read = std::move(f);
}
/**
* @brief Writes data to the terminal
@ -76,7 +81,7 @@ public:
virtual void stop() = 0;
protected:
std::function<bool(uint8_t *data, size_t len)> on_data;
std::function<bool(uint8_t *data, size_t len)> on_read;
std::function<void(terminal_error)> on_error;
};
@ -85,5 +90,3 @@ protected:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_TERMINAL_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_TYPES_HPP_
#define _ESP_MODEM_TYPES_HPP_
#pragma once
#include <functional>
#include <string>
@ -68,6 +67,12 @@ struct PdpContext {
*/
class CommandableIf {
public:
CommandableIf() = default;
CommandableIf(const CommandableIf&) = delete;
CommandableIf& operator=(const CommandableIf&) = delete;
CommandableIf(CommandableIf&&) = delete;
CommandableIf& operator=(CommandableIf&&) = delete;
virtual ~CommandableIf() = default;
/**
* @brief Sends custom AT command
* @param command Command to be sent
@ -84,8 +89,14 @@ public:
*/
class ModuleIf {
public:
ModuleIf() = default;
ModuleIf(const ModuleIf&) = delete;
ModuleIf& operator=(const ModuleIf&) = delete;
ModuleIf(ModuleIf&&) = delete;
ModuleIf& operator=(ModuleIf&&) = delete;
virtual ~ModuleIf() = default;
/**
* @brief Sets the data mode up (provides the necessary configuration to connect to the cellular network
* @brief Sets the data mode up (provides the necessary configuration to connect to the cellular network)
* @return true on success
*/
virtual bool setup_data_mode() = 0;
@ -103,5 +114,3 @@ public:
*/
} // namespace esp_modem
#endif // _ESP_MODEM_TYPES_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_API_H_
#define _ESP_MODEM_API_H_
#pragma once
#include "esp_err.h"
#include "generate/esp_modem_command_declare.inc"
@ -35,6 +34,3 @@ DECLARE_ALL_COMMAND_APIS(declares esp_modem_<API>(esp_modem_t * dce, ...);)
#ifdef __cplusplus
}
#endif
#endif // _CLIENT_ESP_MODEM_API_H_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_C_API_TYPES_H_
#define _ESP_MODEM_C_API_TYPES_H_
#pragma once
#include "esp_modem_config.h"
@ -98,5 +97,3 @@ esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce, esp_modem_dce_mode_t mode);
#ifdef __cplusplus
}
#endif
#endif //_ESP_MODEM_C_API_TYPES_H_

View File

@ -12,11 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_CONFIG_H_
#define _ESP_MODEM_CONFIG_H_
#pragma once
#include "driver/uart.h"
#include "esp_modem_dce_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup ESP_MODEM_CONFIG
* @brief Configuration structures for DTE and DCE
@ -56,6 +60,9 @@ struct esp_modem_uart_term_config {
int event_queue_size; /*!< UART Event Queue Size, set to 0 if no event queue needed */
};
// Forward declare the resource struct
struct esp_modem_vfs_resource;
/**
* @brief VFS configuration structure
*
@ -116,4 +123,6 @@ typedef struct esp_modem_dte_config esp_modem_dte_config_t;
* @}
*/
#endif // _ESP_MODEM_CONFIG_H_
#ifdef __cplusplus
}
#endif

View File

@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_MODEM_DCE_CONFIG_H_
#define _ESP_MODEM_DCE_CONFIG_H_
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup ESP_MODEM_CONFIG
* @{
@ -41,5 +44,6 @@ struct esp_modem_dce_config {
* @}
*/
#endif // _ESP_MODEM_DCE_CONFIG_H_
#ifdef __cplusplus
}
#endif

View File

@ -29,14 +29,14 @@
#define BOOL_OUT(param, name) bool& _ARG(param, name)
#define INT_OUT(param, name) int& _ARG(param, name)
#define STRUCT_OUT(struct_name, x) struct_name& x
#define STRUCT_OUT(struct_name, p1) struct_name& p1
#else
#define STRING_IN(param, name) const char* _ARG(param, name)
#define STRING_OUT(param, name) char* _ARG(param, name)
#define BOOL_IN(param, name) const bool _ARG(param, name)
#define BOOL_OUT(param, name) bool* _ARG(param, name)
#define INT_OUT(param, name) int* _ARG(param, name)
#define STRUCT_OUT(struct_name, x) struct struct_name* x
#define STRUCT_OUT(struct_name, p1) struct struct_name* p1
#endif
#define DECLARE_ALL_COMMAND_APIS(...) \
@ -50,7 +50,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result, 0) \
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, 1, STRING_OUT(x, name)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, 1, STRING_OUT(p1, name)) \
\
/**
* @brief Stores current user profile
@ -63,28 +63,28 @@ ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result, 0) \
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/\
ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(x, pin)) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
\
/**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, 1, BOOL_OUT(x, pin_ok)) \
ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, 1, BOOL_OUT(p1, pin_ok)) \
\
/**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, 1, BOOL_IN(x, echo_on)) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, 1, BOOL_IN(p1, echo_on)) \
\
/**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(sms_txt_mode, command_result, 1, BOOL_IN(x, txt)) \
ESP_MODEM_DECLARE_DCE_COMMAND(sms_txt_mode, command_result, 1, BOOL_IN(p1, txt)) \
\
/**
* @brief Sets the default (GSM) charater set
@ -98,7 +98,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(sms_character_set, command_result, 0) \
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(send_sms, command_result, 2, STRING_IN(x, number), STRING_IN(y, message)) \
ESP_MODEM_DECLARE_DCE_COMMAND(send_sms, command_result, 2, STRING_IN(p1, number), STRING_IN(p2, message)) \
\
/**
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
@ -108,10 +108,10 @@ ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, 0) \
\
/**
* @brief Sets php context
* @param[in] x PdP context struct to setup modem cellular connection
* @param[in] p1 PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, 1, STRUCT_OUT(PdpContext, x)) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, 1, STRUCT_OUT(PdpContext, p1)) \
\
/**
* @brief Switches to the command mode
@ -130,21 +130,21 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result, 0) \
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, 1, STRING_OUT(x, imsi)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, 1, STRING_OUT(p1, imsi)) \
\
/**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, 1, STRING_OUT(x, imei)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, 1, STRING_OUT(p1, imei)) \
\
/**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, 1, STRING_OUT(x, name)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, 1, STRING_OUT(p1, name)) \
\
/**
* @brief Sets the modem to data mode
@ -158,7 +158,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_data_mode, command_result, 0) \
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, INT_OUT(x, rssi), INT_OUT(y, ber)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, INT_OUT(p1, rssi), INT_OUT(p2, ber)) \
\
/**
* @brief Sets HW control flow
@ -166,7 +166,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(get_signal_quality, command_result, 2, INT_OUT(x,
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_flow_control, command_result, 2, INT_IN(x, dce_flow), INT_IN(y, dte_flow)) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_flow_control, command_result, 2, INT_IN(p1, dce_flow), INT_IN(p2, dte_flow)) \
\
/**
* @brief Hangs up current data call
@ -181,7 +181,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(hang_up, command_result, 0) \
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_battery_status, command_result, 3, INT_OUT(x, voltage), INT_OUT(y, bcs), INT_OUT(z, bcl)) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_battery_status, command_result, 3, INT_OUT(p1, voltage), INT_OUT(p2, bcs), INT_OUT(p3, bcl)) \
\
/**
* @brief Power down the module
@ -193,14 +193,14 @@ ESP_MODEM_DECLARE_DCE_COMMAND(power_down, command_result, 0) \
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(reset, command_result, 0)\
ESP_MODEM_DECLARE_DCE_COMMAND(reset, command_result, 0) \
\
/**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(x, baud))
ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(p1, baud))
#ifdef GENERATE_DOCS
@ -213,7 +213,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(x, baud))
#define _ARG(param, name) name
// --- DCE command documentation starts here ---
#ifdef __cplusplus
class esp_modem::DCE: public DCE_T<GenericModule> {
class esp_modem::DCE : public DCE_T<GenericModule> {
public:
using DCE_T<GenericModule>::DCE_T;
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, ...) return_type name (__VA_ARGS__);

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _VFS_CREATE_HPP_
#define _VFS_CREATE_HPP_
#pragma once
#define ESP_MODEM_VFS_DEFAULT_UART_CONFIG(name) { \
.dev_name = (name), \
@ -38,7 +37,7 @@
* @brief UART init struct for VFS
*/
struct esp_modem_vfs_uart_creator {
const char* dev_name; /*!< VFS device name, e.g. /dev/uart/n */
const char *dev_name; /*!< VFS device name, e.g. /dev/uart/n */
const struct esp_modem_uart_term_config uart; /*!< UART driver init struct */
};
@ -46,7 +45,7 @@ struct esp_modem_vfs_uart_creator {
* @brief UART init struct for VFS
*/
struct esp_modem_vfs_socket_creator {
const char* host_name; /*!< VFS socket: host name (or IP address) */
const char *host_name; /*!< VFS socket: host name (or IP address) */
int port; /*!< VFS socket: port number */
};
@ -67,6 +66,3 @@ bool vfs_create_socket(struct esp_modem_vfs_socket_creator *config, struct esp_m
* @return true on success
*/
bool vfs_create_uart(struct esp_modem_vfs_uart_creator *config, struct esp_modem_vfs_term_config *created_config);
#endif //_VFS_CREATE_HPP_

View File

@ -11,8 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MDNS_HOST_ESP_EVENT_H
#define MDNS_HOST_ESP_EVENT_H
#pragma once
#include <stdint.h>
#include "esp_netif.h"
@ -39,5 +38,3 @@ esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id,
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler);
typedef void * QueueHandle_t;
#endif //MDNS_HOST_ESP_EVENT_H

View File

@ -11,8 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MDNS_HOST_ESP_EVENT_BASE_H
#define MDNS_HOST_ESP_EVENT_BASE_H
#pragma once
typedef enum {
WIFI_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
@ -25,4 +24,3 @@ typedef enum {
typedef void * esp_event_base_t;
#endif //MDNS_HOST_ESP_EVENT_BASE_H

View File

@ -3,18 +3,20 @@ set(LWIP_CONTRIB_DIR "$ENV{LWIP_CONTRIB_PATH}")
set(lwipcontribportunix_SRCS ${LWIP_CONTRIB_DIR}/ports/unix/port/sys_arch.c)
include(${LWIP_DIR}/src/Filelists.cmake)
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
set (LWIP_INCLUDE_DIRS
include(${LWIP_DIR}/src/Filelists.cmake)
set (LWIP_INCLUDE_DIRS
"${LWIP_DIR}/src/include"
"${LWIP_CONTRIB_DIR}/ports/unix/port/include")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/netif/slipif.c")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/core/ip4.c")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/core/ipv6/ip6.c")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/netif/slipif.c")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/core/ipv4/ip4.c")
list(REMOVE_ITEM lwipnoapps_SRCS "${LWIP_DIR}/src/core/ipv6/ip6.c")
endif()
idf_component_register(SRCS esp_netif_linux.cpp tun_io.c ip6_stub.c ${lwipnoapps_SRCS} ${lwipcontribportunix_SRCS}
idf_component_register(SRCS esp_netif_linux.cpp tun_io.c ip4_stub.c ip6_stub.c ${lwipnoapps_SRCS} ${lwipcontribportunix_SRCS}
INCLUDE_DIRS include ${LWIP_INCLUDE_DIRS}
PRIV_INCLUDE_DIRS .
REQUIRES esp_system_protocols_linux)

View File

@ -35,8 +35,7 @@ class NetifStorage;
void read_task(NetifStorage *netif);
class NetifStorage: public esp_netif_obj
{
class NetifStorage: public esp_netif_obj {
public:
explicit NetifStorage(const esp_netif_config_t *config) : esp_netif_obj(), exit(false)
{
@ -91,46 +90,9 @@ public:
extern "C" esp_netif_t *esp_netif_new(const esp_netif_config_t *config)
{
return new NetifStorage(config);
// struct ifreq ifr = { };
// esp_netif_t * netif = netif_storage;
// if (netif == NULL) {
// return NULL;
// }
// if ((netif->fd = open(config->dev_name, O_RDWR)) == -1) {
// ESP_LOGE(TAG, "Cannot open %s", config->dev_name);
// goto cleanup;
// }
// ifr.ifr_flags = IFF_TUN;
// strncpy(ifr.ifr_name, config->if_name, IFNAMSIZ);
//
// if (ioctl(netif->fd, TUNSETIFF, (void *)&ifr) == -1) {
// ESP_LOGE(TAG, "Cannot set ioctl TUNSETIFF %m");
// goto cleanup;
// }
// ioctl(netif->fd, TUNSETNOCSUM, 1);
//
// netif->in_buf = new uint8_t[BUF_SIZE];
// netif->out_buf = new uint8_t[BUF_SIZE];
// if (netif->in_buf == nullptr || netif->out_buf == nullptr) {
// goto cleanup;
// }
//
// if (!ppp_netif_init(netif)) {
// ESP_LOGE(TAG, "Cannot initialize pppos lwip netif %m");
// goto cleanup;
// }
//
// return netif;
//
//cleanup:
// close(netif->fd);
// delete[] netif->in_buf;
// delete[] netif->out_buf;
// delete netif_storage;
// return nullptr;
}
void esp_netif_destroy(esp_netif_t *netif)
{
delete static_cast<NetifStorage*>(netif);
delete static_cast<NetifStorage *>(netif);
}

View File

@ -11,8 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _HOST_ESP_NETIF_H_
#define _HOST_ESP_NETIF_H_
#pragma once
#include <stdint.h>
#include <stdlib.h>
@ -63,5 +62,3 @@ void esp_netif_destroy(esp_netif_t *esp_netif);
#ifdef __cplusplus
}
#endif
#endif // _HOST_ESP_NETIF_H_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_NETIF_IP_ADDR_H_
#define _ESP_NETIF_IP_ADDR_H_
#pragma once
#include <endian.h>
@ -164,5 +163,3 @@ esp_ip6_addr_type_t esp_netif_ip6_get_addr_type(esp_ip6_addr_t* ip6_addr);
#ifdef __cplusplus
}
#endif
#endif //_ESP_NETIF_IP_ADDR_H_

View File

@ -13,10 +13,6 @@
// limitations under the License.
#ifndef _ESP_NETIF_PPP_H_
#define _ESP_NETIF_PPP_H_
#pragma once
#define NETIF_PP_PHASE_OFFSET 0x100
#endif //_ESP_NETIF_PPP_H_

View File

@ -0,0 +1,17 @@
#include "lwip/ip4.h"
err_t
ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
{ return ERR_OK; }
struct netif *
ip4_route(const ip4_addr_t *dest)
{ return NULL; }
err_t
ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
{ return ERR_OK; }

View File

@ -11,6 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
typedef int esp_err_t;
#define ESP_FAIL -1

View File

@ -11,8 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MDNS_HOST_ESP_LOG_H
#define MDNS_HOST_ESP_LOG_H
#pragma once
#include <stdio.h>
@ -46,4 +45,3 @@ printf(LOG_COLOR_D); printf("(%s) ", TAG); printf(__VA_ARGS__); printf(LOG_RESET
#define ESP_LOGV(TAG, ...) do { \
printf(LOG_COLOR_V); printf("(%s) ", TAG); printf(__VA_ARGS__); printf(LOG_RESET_COLOR "\n"); } while(0)
#endif //MDNS_HOST_ESP_LOG_H

View File

@ -11,9 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MDNS_HOST_ENDIAN_H
#define MDNS_HOST_ENDIAN_H
#pragma once
#include_next "endian.h"
#endif //MDNS_HOST_ENDIAN_H

View File

@ -0,0 +1,8 @@
#!/bin/bash
apt-get update
apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark
pushd components/esp_modem/docs
./generate_docs
popd

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _EXCEPTION_STUB_HPP_
#define _EXCEPTION_STUB_HPP_
#pragma once
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
#define TRY_CATCH_OR_DO(block, action) \
@ -38,6 +37,3 @@
block
#endif
#endif // _EXCEPTION_STUB_HPP_

View File

@ -12,12 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_log.h"
#include "driver/uart.h"
#ifndef _UART_COMPAT_H_
#define _UART_COMPAT_H_
/**
* @brief This is a compatible header, which just takes care of different data ptr type
* across different IDF version in driver/uart
@ -31,5 +30,3 @@ static inline int uart_write_bytes_compat(uart_port_t uart_num, const void* src,
#endif
return uart_write_bytes(uart_num, data, size);
}
#endif //_UART_COMPAT_H_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _UART_RESOURCE_HPP_
#define _UART_RESOURCE_HPP_
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
@ -26,7 +25,7 @@ namespace esp_modem {
* @brief Uart Resource is a platform specific struct which is implemented separately for ESP_PLATFORM and linux target
*/
struct uart_resource {
explicit uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t* event_queue, int fd);
explicit uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t *event_queue, int fd);
~uart_resource();
@ -35,5 +34,3 @@ struct uart_resource {
} // namespace esp_modem
#endif // _UART_RESOURCE_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _UART_TERMINAL_HPP_
#define _UART_TERMINAL_HPP_
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
@ -25,4 +24,3 @@ std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *confi
} // namespace esp_modem
#endif // _UART_TERMINAL_HPP_

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _VFS_TERMINAL_HPP_
#define _VFS_TERMINAL_HPP_
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
@ -24,5 +23,3 @@ namespace esp_modem {
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config);
} // namespace esp_modem
#endif // _VFS_TERMINAL_HPP_

View File

@ -30,43 +30,43 @@ struct PdpContext;
static const char *TAG = "modem_api";
#endif
std::shared_ptr<DTE> create_uart_dte(const dte_config *config) {
TRY_CATCH_RET_NULL(
auto term = create_uart_terminal(config);
return std::make_shared<DTE>(config, std::move(term));
)
}
std::shared_ptr<DTE> create_vfs_dte(const dte_config *config) {
std::shared_ptr<DTE> create_vfs_dte(const dte_config *config)
{
TRY_CATCH_RET_NULL(
auto term = create_vfs_terminal(config);
return std::make_shared<DTE>(config, std::move(term));
auto term = create_vfs_terminal(config);
return std::make_shared<DTE>(config, std::move(term));
)
}
static inline std::unique_ptr<DCE>
create_modem_dce(dce_factory::Modem m, const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
create_modem_dce(dce_factory::ModemType m, const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
dce_factory::Factory f(m);
TRY_CATCH_RET_NULL(
return f.build_unique(config, std::move(dte), netif);
return f.build_unique(config, std::move(dte), netif);
)
}
std::unique_ptr<DCE> create_SIM7600_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
return create_modem_dce(dce_factory::Modem::SIM7600, config, std::move(dte), netif);
std::unique_ptr<DCE> create_SIM7600_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::SIM7600, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_SIM800_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
return create_modem_dce(dce_factory::Modem::SIM800, config, std::move(dte), netif);
std::unique_ptr<DCE> create_SIM800_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::SIM800, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
return create_modem_dce(dce_factory::Modem::BG96, config, std::move(dte), netif);
std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::BG96, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif) {
return create_modem_dce(dce_factory::Modem::GenericModule, config, std::move(dte), netif);
std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::GenericModule, config, std::move(dte), netif);
}
} // namespace esp_modem

View File

@ -0,0 +1,39 @@
// Copyright 2021 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 <cassert>
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "uart_terminal.hpp"
#include "vfs_termial.hpp"
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_factory.hpp"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
namespace esp_modem {
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
static const char *TAG = "modem_api_target";
#endif
std::shared_ptr<DTE> create_uart_dte(const dte_config *config)
{
TRY_CATCH_RET_NULL(
auto term = create_uart_terminal(config);
return std::make_shared<DTE>(config, std::move(term));
)
}
} // namespace esp_modem

View File

@ -23,50 +23,58 @@
#include "exception_stub.hpp"
#include "cstring"
#ifndef ESP_MODEM_C_API_STR_MAX
#define ESP_MODEM_C_API_STR_MAX 64
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dest, const char *src, size_t len);
#endif
//
// C API definitions
using namespace esp_modem;
struct esp_modem_dce_wrap // need to mimic the polymorphic dispatch as CPP uses templated dispatch
{
struct esp_modem_dce_wrap { // need to mimic the polymorphic dispatch as CPP uses templated dispatch
enum class modem_wrap_dte_type { UART, } dte_type;
dce_factory::Modem modem_type;
DCE* dce;
dce_factory::ModemType modem_type;
DCE *dce;
};
static inline esp_err_t command_response_to_esp_err(command_result res)
{
switch (res) {
case command_result::OK:
return ESP_OK;
case command_result::FAIL:
return ESP_FAIL;
case command_result::TIMEOUT:
return ESP_ERR_TIMEOUT;
case command_result::OK:
return ESP_OK;
case command_result::FAIL:
return ESP_FAIL;
case command_result::TIMEOUT:
return ESP_ERR_TIMEOUT;
}
return ESP_ERR_INVALID_ARG;
}
static inline dce_factory::Modem convert_modem_enum(esp_modem_dce_device_t module)
static inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t module)
{
switch (module) {
case ESP_MODEM_DCE_SIM7600:
return esp_modem::dce_factory::Modem::SIM7600;
case ESP_MODEM_DCE_BG96:
return esp_modem::dce_factory::Modem::BG96;
case ESP_MODEM_DCE_SIM800:
return esp_modem::dce_factory::Modem::SIM800;
default:
case ESP_MODEM_DCE_GENETIC:
return esp_modem::dce_factory::Modem::GenericModule;
case ESP_MODEM_DCE_SIM7600:
return esp_modem::dce_factory::ModemType::SIM7600;
case ESP_MODEM_DCE_BG96:
return esp_modem::dce_factory::ModemType::BG96;
case ESP_MODEM_DCE_SIM800:
return esp_modem::dce_factory::ModemType::SIM800;
default:
case ESP_MODEM_DCE_GENETIC:
return esp_modem::dce_factory::ModemType::GenericModule;
}
}
extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
{
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
if (dce_wrap == nullptr)
if (dce_wrap == nullptr) {
return nullptr;
}
auto dte = create_uart_dte(dte_config);
if (dte == nullptr) {
delete dce_wrap;
@ -88,7 +96,7 @@ extern "C" esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_conf
return esp_modem_new_dev(ESP_MODEM_DCE_GENETIC, dte_config, dce_config, netif);
}
extern "C" void esp_modem_destroy(esp_modem_dce_t * dce_wrap)
extern "C" void esp_modem_destroy(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap) {
delete dce_wrap->dce;
@ -96,10 +104,11 @@ extern "C" void esp_modem_destroy(esp_modem_dce_t * dce_wrap)
}
}
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce_wrap, esp_modem_dce_mode_t mode)
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce_mode_t mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (mode == ESP_MODEM_MODE_DATA) {
dce_wrap->dce->set_data();
} else if (mode == ESP_MODEM_MODE_COMMAND) {
@ -110,62 +119,147 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce_wrap, esp_modem_dc
return ESP_OK;
}
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t * dce_wrap, bool *pin)
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce_wrap, bool *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
}
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t * dce_wrap, bool txt)
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce_wrap, bool txt)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_txt_mode(txt));
}
extern "C" esp_err_t esp_modem_send_sms(esp_modem_dce_t * dce_wrap, const char * number, const char * message)
extern "C" esp_err_t esp_modem_send_sms(esp_modem_dce_t *dce_wrap, const char *number, const char *message)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string number_str(number);
std::string message_str(message);
return command_response_to_esp_err(dce_wrap->dce->send_sms(number_str, message_str));
}
extern "C" esp_err_t esp_modem_sms_character_set(esp_modem_dce_t * dce_wrap)
extern "C" esp_err_t esp_modem_sms_character_set(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_character_set());
}
extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t * dce_wrap, const char *pin)
extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce_wrap, const char *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string pin_str(pin);
return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str));
}
extern "C" esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t * dce_wrap, int *rssi, int *ber)
extern "C" esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t *dce_wrap, int *rssi, int *ber)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->get_signal_quality(*rssi, *ber));
}
extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t * dce_wrap, char *p_imsi)
extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce_wrap, char *p_imsi)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr)
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imsi;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imsi(imsi));
if (ret == ESP_OK && !imsi.empty()) {
strcpy(p_imsi, imsi.c_str());
strlcpy(p_imsi, imsi.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
}
extern "C" esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce_wrap, int dce_flow, int dte_flow)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_flow_control(dce_flow, dte_flow));
}
extern "C" esp_err_t esp_modem_store_profile(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->store_profile());
}
extern "C" esp_err_t esp_modem_get_imei(esp_modem_dce_t *dce_wrap, char *p_imei)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imei;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imei(imei));
if (ret == ESP_OK && !imei.empty()) {
strlcpy(p_imei, imei.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce_wrap, char *p_name)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_operator_name(name));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_module_name(esp_modem_dce_t *dce_wrap, char *p_name)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_module_name(name));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_battery_status(esp_modem_dce_t *dce_wrap, int *p_volt, int *p_bcs, int *p_bcl)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_bcs == nullptr || p_bcl == nullptr || p_volt == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int bcs, bcl, volt;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_battery_status(volt, bcs, bcl));
if (ret == ESP_OK) {
*p_volt = volt;
*p_bcs = bcs;
*p_bcl = bcl;
}
return ret;
}
extern "C" esp_err_t esp_modem_power_down(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->power_down());
}

View File

@ -91,11 +91,21 @@ void CMux::send_sabm(size_t i)
}
struct CMux::CMuxFrame {
uint8_t *ptr; /*!< pointer to the currently processing byte of the CMUX frame */
size_t len; /*!< length of available data in the current CMUX frame */
void advance(int size = 1)
{
ptr += size;
len -= size;
}
};
void CMux::data_available(uint8_t *data, size_t len)
{
if (data && type == 0xFF && len > 0 && dlci > 0) {
int virtual_term = dlci - 1;
if (virtual_term < max_terms && read_cb[virtual_term]) {
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
// Post partial data (or defragment to post on CMUX footer)
#ifdef DEFRAGMENT_CMUX_PAYLOAD
if (payload_start == nullptr) {
@ -112,15 +122,126 @@ void CMux::data_available(uint8_t *data, size_t len)
sabm_ack = dlci;
} else if (data == nullptr) {
int virtual_term = dlci - 1;
if (virtual_term < max_terms && read_cb[virtual_term]) {
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
#ifdef DEFRAGMENT_CMUX_PAYLOAD
read_cb[virtual_term](payload_start, total_payload_size);
#endif
}
}
}
bool CMux::on_init(CMuxFrame &frame)
{
if (frame.ptr[0] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed leading SOF, recovering...");
state = cmux_state::RECOVER;
return true;
}
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
// empty frame
frame.advance();
return true;
}
state = cmux_state::HEADER;
frame.advance();
frame_header_offset = 1;
return true;
}
bool CMux::on_recovery(CMuxFrame &frame)
{
uint8_t *recover_ptr;
if (frame.ptr[0] == SOF_MARKER) {
// already init state
state = cmux_state::INIT;
return true;
}
recover_ptr = static_cast<uint8_t *>(memchr(frame.ptr, SOF_MARKER, frame.len));
if (recover_ptr && frame.len > recover_ptr - frame.ptr) {
frame.len -= (recover_ptr - frame.ptr);
frame.ptr = recover_ptr;
state = cmux_state::INIT;
ESP_LOGI("CMUX", "Protocol recovered");
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
// empty frame
frame.advance();
}
return true;
}
// marker not found, continue with recovery
return false;
}
bool CMux::on_header(CMuxFrame &frame)
{
if (frame.len > 0 && frame_header_offset == 1 && frame.ptr[0] == SOF_MARKER) {
// Previously trailing SOF interpreted as heading SOF, remove it and restart HEADER
frame.advance();
return true;
}
if (frame.len + frame_header_offset < 4) {
memcpy(frame_header + frame_header_offset, frame.ptr, frame.len);
frame_header_offset += frame.len;
return false; // need read more
}
size_t payload_offset = std::min(frame.len, 4 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
frame_header_offset += payload_offset;
dlci = frame_header[1] >> 2;
type = frame_header[2];
payload_len = (frame_header[3] >> 1);
frame.advance(payload_offset);
state = cmux_state::PAYLOAD;
return true;
}
bool CMux::on_payload(CMuxFrame &frame)
{
ESP_LOGD("CMUX", "Payload frame: dlci:%02x type:%02x payload:%d available:%d", dlci, type, payload_len, frame.len);
if (frame.len < payload_len) { // payload
state = cmux_state::PAYLOAD;
data_available(frame.ptr, frame.len); // partial read
payload_len -= frame.len;
return false;
} else { // complete
if (payload_len > 0) {
data_available(&frame.ptr[0], payload_len); // rest read
}
frame.advance((payload_len));
state = cmux_state::FOOTER;
payload_len = 0;
}
return true;
}
bool CMux::on_footer(CMuxFrame &frame)
{
size_t footer_offset = 0;
if (frame.len + frame_header_offset < 6) {
memcpy(frame_header + frame_header_offset, frame.ptr, frame.len);
frame_header_offset += frame.len;
return false; // need read more
} else {
footer_offset = std::min(frame.len, 6 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, footer_offset);
if (frame_header[5] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
payload_start = nullptr;
total_payload_size = 0;
state = cmux_state::RECOVER;
return true;
}
frame.advance(footer_offset);
state = cmux_state::INIT;
frame_header_offset = 0;
data_available(nullptr, 0);
payload_start = nullptr;
total_payload_size = 0;
}
return true;
}
bool CMux::on_cmux(uint8_t *data, size_t actual_len)
{
if (!data) {
@ -138,115 +259,35 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
actual_len = term->read(data, buffer_size);
#endif
}
ESP_LOG_BUFFER_HEXDUMP("CMUX Received", data, actual_len, ESP_LOG_DEBUG);
uint8_t* frame = data;
uint8_t* recover_ptr;
auto available_len = actual_len;
size_t payload_offset = 0;
size_t footer_offset = 0;
while (available_len > 0) {
ESP_LOG_BUFFER_HEXDUMP("CMUX Received", data, actual_len, ESP_LOG_VERBOSE);
CMuxFrame frame = { .ptr = data, .len = actual_len };
while (frame.len > 0) {
switch (state) {
case cmux_state::RECOVER:
if (frame[0] == SOF_MARKER) {
// already init state
state = cmux_state::INIT;
break;
}
recover_ptr = static_cast<uint8_t*>(memchr(frame, SOF_MARKER, available_len));
if (recover_ptr && available_len > recover_ptr - frame) {
available_len -= (recover_ptr - frame);
frame = recover_ptr;
state = cmux_state::INIT;
ESP_LOGI("CMUX", "Protocol recovered");
if (available_len > 1 && frame[1] == SOF_MARKER) {
// empty frame
available_len -= 1;
frame += 1;
}
break;
}
// marker not found, continue with recovery
case cmux_state::RECOVER:
if (!on_recovery(frame)) {
return false;
case cmux_state::INIT:
if (frame[0] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed leading SOF, recovering...");
state = cmux_state::RECOVER;
break;
}
if (available_len > 1 && frame[1] == SOF_MARKER) {
// empty frame
available_len -= 1;
frame += 1;
break;
}
state = cmux_state::HEADER;
available_len--;
frame_header_offset = 1;
frame++;
break;
case cmux_state::HEADER:
if (available_len > 0 && frame_header_offset == 1 && frame[0] == SOF_MARKER) {
// Previously trailing SOF interpreted as heading SOF, remove it and restart HEADER
available_len--;
frame++;
break;
}
if (available_len + frame_header_offset < 4) {
memcpy(frame_header + frame_header_offset, frame, available_len);
frame_header_offset += available_len;
return false; // need read more
}
payload_offset = std::min(available_len, 4 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame, payload_offset);
frame_header_offset += payload_offset;
dlci = frame_header[1] >> 2;
type = frame_header[2];
payload_len = (frame_header[3] >> 1);
frame += payload_offset;
available_len -= payload_offset;
state = cmux_state::PAYLOAD;
break;
case cmux_state::PAYLOAD:
ESP_LOGD("CMUX", "Payload frame: dlci:%02x type:%02x payload:%d available:%d", dlci, type, payload_len, available_len);
if (available_len < payload_len) { // payload
state = cmux_state::PAYLOAD;
data_available(frame, available_len); // partial read
payload_len -= available_len;
return false;
} else { // complete
if (payload_len > 0) {
data_available(&frame[0], payload_len); // rest read
}
available_len -= payload_len;
frame += payload_len;
state = cmux_state::FOOTER;
payload_len = 0;
}
break;
case cmux_state::FOOTER:
if (available_len + frame_header_offset < 6) {
memcpy(frame_header + frame_header_offset, frame, available_len);
frame_header_offset += available_len;
return false; // need read more
} else {
footer_offset = std::min(available_len, 6 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame, footer_offset);
if (frame_header[5] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
payload_start = nullptr;
total_payload_size = 0;
state = cmux_state::RECOVER;
break;
}
frame += footer_offset;
available_len -= footer_offset;
state = cmux_state::INIT;
frame_header_offset = 0;
data_available(nullptr, 0);
payload_start = nullptr;
total_payload_size = 0;
}
break;
}
break;
case cmux_state::INIT:
if (!on_init(frame)) {
return false;
}
break;
case cmux_state::HEADER:
if (!on_header(frame)) {
return false;
}
break;
case cmux_state::PAYLOAD:
if (!on_payload(frame)) {
return false;
}
break;
case cmux_state::FOOTER:
if (!on_footer(frame)) {
return false;
}
break;
}
}
return true;
@ -262,8 +303,7 @@ bool CMux::init()
});
sabm_ack = -1;
for (size_t i = 0; i < 3; i++)
{
for (size_t i = 0; i < 3; i++) {
int timeout = 0;
send_sabm(i);
while (true) {
@ -273,8 +313,9 @@ bool CMux::init()
sabm_ack = -1;
break;
}
if (timeout++ > 100)
if (timeout++ > 100) {
return false;
}
}
}
return true;
@ -288,8 +329,9 @@ int CMux::write(int virtual_term, uint8_t *data, size_t len)
size_t need_write = len;
while (need_write > 0) {
size_t batch_len = need_write;
if (batch_len > cmux_max_len)
if (batch_len > cmux_max_len) {
batch_len = cmux_max_len;
}
uint8_t frame[6];
frame[0] = SOF_MARKER;
frame[1] = (i << 2) + 1;
@ -303,14 +345,16 @@ int CMux::write(int virtual_term, uint8_t *data, size_t len)
term->write(frame + 4, 2);
ESP_LOG_BUFFER_HEXDUMP("Send", frame, 4, ESP_LOG_VERBOSE);
ESP_LOG_BUFFER_HEXDUMP("Send", data, batch_len, ESP_LOG_VERBOSE);
ESP_LOG_BUFFER_HEXDUMP("Send", frame+4, 2, ESP_LOG_VERBOSE);
ESP_LOG_BUFFER_HEXDUMP("Send", frame + 4, 2, ESP_LOG_VERBOSE);
need_write -= batch_len;
data += batch_len;
}
return len;
}
void CMux::set_read_cb(int inst, std::function<bool(uint8_t *, size_t)> f) {
if (inst < max_terms)
void CMux::set_read_cb(int inst, std::function<bool(uint8_t *, size_t)> f)
{
if (inst < MAX_TERMINALS_NUM) {
read_cb[inst] = std::move(f);
}
}

View File

@ -23,49 +23,53 @@ namespace esp_modem::dce_commands {
static const char *TAG = "command_lib";
command_result generic_command(CommandableIf* t, const std::string &command,
const std::list<std::string_view>& pass_phrase,
const std::list<std::string_view>& fail_phrase,
uint32_t timeout_ms)
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
uint32_t timeout_ms)
{
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
return t->command(command, [&](uint8_t *data, size_t len) {
std::string_view response((char*)data, len);
if (data == nullptr || len == 0 || response.empty())
std::string_view response((char *)data, len);
if (data == nullptr || len == 0 || response.empty()) {
return command_result::TIMEOUT;
}
ESP_LOGD(TAG, "Response: %.*s\n", (int)response.length(), response.data());
for (auto &it : pass_phrase)
if (response.find(it) != std::string::npos)
if (response.find(it) != std::string::npos) {
return command_result::OK;
}
for (auto &it : fail_phrase)
if (response.find(it) != std::string::npos)
if (response.find(it) != std::string::npos) {
return command_result::FAIL;
}
return command_result::TIMEOUT;
}, timeout_ms);
}
static inline command_result generic_command(CommandableIf* t, const std::string& command,
const std::string& pass_phrase,
const std::string& fail_phrase, uint32_t timeout_ms)
static inline command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
const auto pass = std::list<std::string_view>({pass_phrase});
const auto fail = std::list<std::string_view>({fail_phrase});
return generic_command(t, command, pass, fail, timeout_ms);
}
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string_view& output, uint32_t timeout_ms = 500)
static inline command_result generic_get_string(CommandableIf *t, const std::string &command, std::string_view &output, uint32_t timeout_ms = 500)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return t->command(command, [&](uint8_t *data, size_t len) {
size_t pos = 0;
std::string_view response((char*)data, len);
std::string_view response((char *)data, len);
while ((pos = response.find('\n')) != std::string::npos) {
std::string_view token = response.substr(0, pos);
for (auto it = token.end() - 1; it > token.begin(); it--) // strip trailing CR or LF
if (*it == '\r' || *it == '\n')
if (*it == '\r' || *it == '\n') {
token.remove_suffix(1);
}
ESP_LOGV(TAG, "Token: {%.*s}\n", static_cast<int>(token.size()), token.data());
if (token.find("OK") != std::string::npos) {
@ -75,147 +79,157 @@ static inline command_result generic_get_string(CommandableIf* t, const std::str
} else if (token.size() > 2) {
output = token;
}
response = response.substr(pos+1);
response = response.substr(pos + 1);
}
return command_result::TIMEOUT;
}, timeout_ms);
}
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500)
static inline command_result generic_get_string(CommandableIf *t, const std::string &command, std::string &output, uint32_t timeout_ms = 500)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, command, out, timeout_ms);
if (ret == command_result::OK)
if (ret == command_result::OK) {
output = out;
}
return ret;
}
static inline command_result generic_command_common(CommandableIf* t, const std::string &command, uint32_t timeout = 500)
static inline command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout = 500)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, command, "OK", "ERROR", timeout);
}
command_result sync(CommandableIf* t)
command_result sync(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT\r");
}
command_result store_profile(CommandableIf* t)
command_result store_profile(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT&W\r");
}
command_result power_down(CommandableIf* t)
command_result power_down(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "AT+QPOWD=1\r", "POWERED DOWN", "ERROR", 1000);
}
command_result power_down_sim7xxx(CommandableIf* t)
command_result power_down_sim7xxx(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CPOF\r", 1000);
}
command_result power_down_sim8xx(CommandableIf* t)
command_result power_down_sim8xx(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
}
command_result reset(CommandableIf* t)
command_result reset(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "AT+CRESET\r", "PB DONE", "ERROR", 60000);
}
command_result set_baud(CommandableIf* t, int baud)
command_result set_baud(CommandableIf *t, int baud)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+IPR=" + std::to_string(baud) + "\r");
}
command_result hang_up(CommandableIf* t)
command_result hang_up(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "ATH\r", 90000);
}
command_result get_battery_status(CommandableIf* t, int& voltage, int &bcs, int &bcl)
command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int &bcl)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CBC\r", out);
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CBC: ";
if (out.find(pattern) == std::string_view::npos)
if (out.find(pattern) == std::string_view::npos) {
return command_result::FAIL;
}
// Parsing +CBC: <bcs>,<bcl>,<voltage>
out = out.substr(pattern.size());
int pos, value, property = 0;
while ((pos = out.find(',') != std::string::npos)) {
if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument)
while ((pos = out.find(',')) != std::string::npos) {
if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
switch (property++) {
case 0: bcs = value;
break;
case 1: bcl = value;
break;
default:
return command_result::FAIL;
case 0: bcs = value;
break;
case 1: bcl = value;
break;
default:
return command_result::FAIL;
}
out = out.substr(pos + 1);
}
if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument)
if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}
command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl)
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CBC\r", out);
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
}
// Parsing +CBC: <voltage in Volts> V
constexpr std::string_view pattern = "+CBC: ";
constexpr int num_pos = pattern.size();
int dot_pos;
if (out.find(pattern) == std::string::npos ||
(dot_pos = out.find('.')) == std::string::npos)
(dot_pos = out.find('.')) == std::string::npos) {
return command_result::FAIL;
}
int volt, fraction;
if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument)
if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument) {
return command_result::FAIL;
if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument)
}
if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
bcl = bcs = -1; // not available for these models
voltage = 1000*volt + fraction;
voltage = 1000 * volt + fraction;
return command_result::OK;
}
command_result set_flow_control(CommandableIf* t, int dce_flow, int dte_flow)
command_result set_flow_control(CommandableIf *t, int dce_flow, int dte_flow)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+IFC=" + std::to_string(dce_flow) + ", " + std::to_string(dte_flow) + "\r");
}
command_result get_operator_name(CommandableIf* t, std::string& operator_name)
command_result get_operator_name(CommandableIf *t, std::string &operator_name)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
}
auto pos = out.find("+COPS");
auto property = 0;
while (pos != std::string::npos) {
@ -229,113 +243,118 @@ command_result get_operator_name(CommandableIf* t, std::string& operator_name)
return command_result::FAIL;
}
command_result set_echo(CommandableIf* t, bool on)
command_result set_echo(CommandableIf *t, bool on)
{
ESP_LOGV(TAG,"%s", __func__ );
if (on)
ESP_LOGV(TAG, "%s", __func__ );
if (on) {
return generic_command_common(t, "ATE1\r");
}
return generic_command_common(t, "ATE0\r");
}
command_result set_pdp_context(CommandableIf* t, PdpContext& pdp)
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string pdp_command = "AT+CGDCONT=" + std::to_string(pdp.context_id) +
",\"" + pdp.protocol_type + "\",\"" + pdp.apn + "\"\r";
return generic_command_common(t, pdp_command);
}
command_result set_data_mode(CommandableIf* t)
command_result set_data_mode(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
}
command_result set_data_mode_sim8xx(CommandableIf* t)
command_result set_data_mode_sim8xx(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
}
command_result resume_data_mode(CommandableIf* t)
command_result resume_data_mode(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "ATO\r", "CONNECT", "ERROR", 5000);
}
command_result set_command_mode(CommandableIf* t)
command_result set_command_mode(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
const auto fail = std::list<std::string_view>({"ERROR"});
return generic_command(t, "+++", pass, fail, 5000);
}
command_result get_imsi(CommandableIf* t, std::string& imsi_number)
command_result get_imsi(CommandableIf *t, std::string &imsi_number)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_get_string(t, "AT+CIMI\r", imsi_number, 5000);
}
command_result get_imei(CommandableIf* t, std::string& out)
command_result get_imei(CommandableIf *t, std::string &out)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_get_string(t, "AT+CGSN\r", out, 5000);
}
command_result get_module_name(CommandableIf* t, std::string& out)
command_result get_module_name(CommandableIf *t, std::string &out)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_get_string(t, "AT+CGMM\r", out, 5000);
}
command_result sms_txt_mode(CommandableIf* t, bool txt = true)
command_result sms_txt_mode(CommandableIf *t, bool txt = true)
{
ESP_LOGV(TAG,"%s", __func__ );
if (txt)
return generic_command_common(t, "AT+CMGF=1\r"); // Text mode (default)
ESP_LOGV(TAG, "%s", __func__ );
if (txt) {
return generic_command_common(t, "AT+CMGF=1\r"); // Text mode (default)
}
return generic_command_common(t, "AT+CMGF=0\r"); // PDU mode
}
command_result sms_character_set(CommandableIf* t)
command_result sms_character_set(CommandableIf *t)
{
// Sets the default GSM character set
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CSCS=\"GSM\"\r");
}
command_result send_sms(CommandableIf* t, const std::string& number, const std::string& message)
command_result send_sms(CommandableIf *t, const std::string &number, const std::string &message)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
std::string_view response((char*)data, len);
ESP_LOGD(TAG,"Send SMS response %.*s", static_cast<int>(response.size()), response.data());
std::string_view response((char *)data, len);
ESP_LOGD(TAG, "Send SMS response %.*s", static_cast<int>(response.size()), response.data());
if (response.find('>') != std::string::npos) {
return command_result::OK;
}
return command_result::TIMEOUT;
}, 5000, ' ');
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
return generic_command_common(t, message +"\x1A", 120000);
}
return generic_command_common(t, message + "\x1A", 120000);
}
command_result set_cmux(CommandableIf* t)
command_result set_cmux(CommandableIf *t)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CMUX=0\r");
}
command_result read_pin(CommandableIf* t, bool& pin_ok)
command_result read_pin(CommandableIf *t, bool &pin_ok)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
if (out.find("+CPIN:") == std::string::npos)
}
if (out.find("+CPIN:") == std::string::npos) {
return command_result::FAIL;
}
if (out.find("SIM PIN") != std::string::npos || out.find("SIM PUK") != std::string::npos) {
pin_ok = false;
return command_result::OK;
@ -347,32 +366,36 @@ command_result read_pin(CommandableIf* t, bool& pin_ok)
return command_result::FAIL; // Neither pin-ok, nor waiting for pin/puk -> mark as error
}
command_result set_pin(CommandableIf* t, const std::string& pin)
command_result set_pin(CommandableIf *t, const std::string &pin)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
return generic_command_common(t, set_pin_command);
}
command_result get_signal_quality(CommandableIf* t, int &rssi, int &ber)
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
{
ESP_LOGV(TAG,"%s", __func__ );
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CSQ\r", out);
if (ret != command_result::OK)
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CSQ: ";
constexpr int rssi_pos = pattern.size();
int ber_pos;
if (out.find(pattern) == std::string::npos ||
(ber_pos = out.find(',')) == std::string::npos)
(ber_pos = out.find(',')) == std::string::npos) {
return command_result::FAIL;
}
if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument)
if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument) {
return command_result::FAIL;
if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument)
}
if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}

View File

@ -22,44 +22,53 @@ namespace esp_modem {
bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
{
switch (m) {
case modem_mode::UNDEF:
break;
case modem_mode::COMMAND_MODE:
if (mode == modem_mode::COMMAND_MODE)
return false;
netif.stop();
if (!device->set_mode(modem_mode::COMMAND_MODE))
return false;
dte->set_read_cb([&](uint8_t *data, size_t len) -> bool {
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_INFO);
return false;
});
netif.wait_until_ppp_exits();
dte->set_read_cb(nullptr);
if (!dte->set_mode(modem_mode::COMMAND_MODE))
return false;
mode = m;
return true;
case modem_mode::DATA_MODE:
if (mode == modem_mode::DATA_MODE)
return false;
if (!device->setup_data_mode())
return false;
if (!device->set_mode(modem_mode::DATA_MODE))
return false;
if (!dte->set_mode(modem_mode::DATA_MODE))
return false;
netif.start();
mode = m;
return true;
case modem_mode::CMUX_MODE:
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE)
return false;
device->set_mode(modem_mode::CMUX_MODE);
if (!dte->set_mode(modem_mode::CMUX_MODE))
return false;
mode = modem_mode::COMMAND_MODE;
return true;
case modem_mode::UNDEF:
break;
case modem_mode::COMMAND_MODE:
if (mode == modem_mode::COMMAND_MODE) {
return false;
}
netif.stop();
if (!device->set_mode(modem_mode::COMMAND_MODE)) {
return false;
}
dte->set_read_cb([&](uint8_t *data, size_t len) -> bool {
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_INFO);
return false;
});
netif.wait_until_ppp_exits();
dte->set_read_cb(nullptr);
if (!dte->set_mode(modem_mode::COMMAND_MODE)) {
return false;
}
mode = m;
return true;
case modem_mode::DATA_MODE:
if (mode == modem_mode::DATA_MODE) {
return false;
}
if (!device->setup_data_mode()) {
return false;
}
if (!device->set_mode(modem_mode::DATA_MODE)) {
return false;
}
if (!dte->set_mode(modem_mode::DATA_MODE)) {
return false;
}
netif.start();
mode = m;
return true;
case modem_mode::CMUX_MODE:
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) {
return false;
}
device->set_mode(modem_mode::CMUX_MODE);
if (!dte->set_mode(modem_mode::CMUX_MODE)) {
return false;
}
mode = modem_mode::COMMAND_MODE;
return true;
}
return false;
}

View File

@ -22,10 +22,10 @@ using namespace esp_modem;
static const size_t dte_default_buffer_size = 1000;
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
buffer_size(config->dte_buffer_size), consumed(0),
buffer(std::make_unique<uint8_t[]>(buffer_size)),
term(std::move(terminal)), command_term(term.get()), other_term(nullptr),
mode(modem_mode::UNDEF) {}
buffer_size(config->dte_buffer_size), consumed(0),
buffer(std::make_unique<uint8_t[]>(buffer_size)),
term(std::move(terminal)), command_term(term.get()), other_term(nullptr),
mode(modem_mode::UNDEF) {}
DTE::DTE(std::unique_ptr<Terminal> terminal):
buffer_size(dte_default_buffer_size), consumed(0),
@ -72,17 +72,21 @@ command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32
bool DTE::setup_cmux()
{
auto original_term = std::move(term);
if (original_term == nullptr)
if (original_term == nullptr) {
return false;
}
auto cmux_term = std::make_shared<CMux>(std::move(original_term), std::move(buffer), buffer_size);
if (cmux_term == nullptr)
if (cmux_term == nullptr) {
return false;
}
buffer_size = 0;
if (!cmux_term->init())
if (!cmux_term->init()) {
return false;
}
term = std::make_unique<CMuxInstance>(cmux_term, 0);
if (term == nullptr)
if (term == nullptr) {
return false;
}
command_term = term.get(); // use command terminal as previously
other_term = std::make_unique<CMuxInstance>(cmux_term, 1);
return true;
@ -110,8 +114,9 @@ void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
data = buffer.get();
len = term->read(buffer.get(), buffer_size);
}
if (on_data)
if (on_data) {
return on_data(data, len);
}
return false;
});
}

View File

@ -20,18 +20,18 @@
namespace esp_modem {
template<typename T>
std::shared_ptr<T> create_device(const std::shared_ptr<DTE>& dte, std::string &apn)
std::shared_ptr<T> create_device(const std::shared_ptr<DTE> &dte, std::string &apn)
{
auto pdp = std::make_unique<PdpContext>(apn);
return std::make_shared<T>(dte, std::move(pdp));
}
std::shared_ptr<GenericModule> create_generic_module(const std::shared_ptr<DTE>& dte, std::string &apn)
std::shared_ptr<GenericModule> create_generic_module(const std::shared_ptr<DTE> &dte, std::string &apn)
{
return create_device<GenericModule>(dte, apn);
}
std::shared_ptr<SIM7600> create_SIM7600_module(const std::shared_ptr<DTE>& dte, std::string &apn)
std::shared_ptr<SIM7600> create_SIM7600_module(const std::shared_ptr<DTE> &dte, std::string &apn)
{
return create_device<SIM7600>(dte, apn);
}
@ -42,7 +42,8 @@ std::shared_ptr<SIM7600> create_SIM7600_module(const std::shared_ptr<DTE>& dte,
#include "cxx_include/esp_modem_dce_factory.hpp"
namespace esp_modem::dce_factory {
std::unique_ptr<PdpContext> FactoryHelper::create_pdp_context(std::string &apn) {
std::unique_ptr<PdpContext> FactoryHelper::create_pdp_context(std::string &apn)
{
return std::unique_ptr<PdpContext>();
}

View File

@ -19,7 +19,7 @@
namespace esp_modem {
GenericModule::GenericModule(std::shared_ptr<DTE> dte, const dce_config *config) :
dte(std::move(dte)), pdp(std::make_unique<PdpContext>(config->apn)) {}
dte(std::move(dte)), pdp(std::make_unique<PdpContext>(config->apn)) {}
//
// Define preprocessor's forwarding to dce_commands definitions
@ -27,9 +27,13 @@ GenericModule::GenericModule(std::shared_ptr<DTE> dte, const dce_config *config)
// Helper macros to handle multiple arguments of declared API
#define ARGS0
#define ARGS1 , x
#define ARGS2 , x , y
#define ARGS3 , x , y , z
#define ARGS1 , p1
#define ARGS2 , p1 , p2
#define ARGS3 , p1 , p2 , p3
#define ARGS4 , p1 , p2 , p3, p4
#define ARGS5 , p1 , p2 , p3, p4, p5
#define ARGS6 , p1 , p2 , p3, p4, p5, p6
#define _ARGS(x) ARGS ## x
#define ARGS(x) _ARGS(x)
@ -46,35 +50,24 @@ DECLARE_ALL_COMMAND_APIS(return_type name(...) )
//
// Handle specific commands for specific supported modems
//
command_result SIM7600::get_module_name(std::string &name) {
name = "7600";
return command_result::OK;
}
command_result SIM7600::get_battery_status(int& voltage, int &bcs, int &bcl) {
command_result SIM7600::get_battery_status(int &voltage, int &bcs, int &bcl)
{
return dce_commands::get_battery_status_sim7xxx(dte.get(), voltage, bcs, bcl);
}
command_result SIM7600::power_down() {
command_result SIM7600::power_down()
{
return dce_commands::power_down_sim7xxx(dte.get());
}
command_result SIM800::get_module_name(std::string &name) {
name = "800L";
return command_result::OK;
}
command_result SIM800::power_down() {
command_result SIM800::power_down()
{
return dce_commands::power_down_sim8xx(dte.get());
}
command_result SIM800::set_data_mode() {
command_result SIM800::set_data_mode()
{
return dce_commands::set_data_mode_sim8xx(dte.get());
}
command_result BG96::get_module_name(std::string &name) {
name = "BG96";
return command_result::OK;
}
}

View File

@ -24,7 +24,8 @@
namespace esp_modem {
void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
int32_t event_id, void *event_data)
{
auto *ppp = static_cast<Netif *>(arg);
if (event_id < NETIF_PP_PHASE_OFFSET) {
ESP_LOGI("esp_modem_netif", "PPP state changed event %d", event_id);
@ -33,7 +34,8 @@ void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
}
}
esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len) {
esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len)
{
auto *ppp = static_cast<Netif *>(h);
if (ppp->signal.is_any(PPP_STARTED)) {
if (ppp->ppp_dte && ppp->ppp_dte->write((uint8_t *) buffer, len) > 0) {
@ -43,7 +45,8 @@ esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len) {
return ESP_FAIL;
}
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
{
auto d = (ppp_netif_driver *) args;
esp_netif_driver_ifconfig_t driver_ifconfig = {};
driver_ifconfig.transmit = Netif::esp_modem_dte_transmit;
@ -53,7 +56,8 @@ 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
// to notify the modem layer when switching modes
esp_netif_ppp_config_t ppp_config = { .ppp_phase_event_enabled = true, // assuming phase enabled, as earlier IDFs
.ppp_error_event_enabled = false }; // don't provide cfg getters so we enable both events
.ppp_error_event_enabled = false
}; // don't provide cfg getters so we enable both events
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 4
esp_netif_ppp_get_params(esp_netif, &ppp_config);
#endif // ESP-IDF >= v4.4
@ -65,25 +69,28 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
return ESP_OK;
}
void Netif::receive(uint8_t *data, size_t len) {
void Netif::receive(uint8_t *data, size_t len)
{
if (signal.is_any(PPP_STARTED)) {
esp_netif_receive(driver.base.netif, data, len, nullptr);
}
}
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
ppp_dte(std::move(e)), netif(ppp_netif) {
ppp_dte(std::move(e)), netif(ppp_netif)
{
driver.base.netif = ppp_netif;
driver.ppp = this;
driver.base.post_attach = esp_modem_post_attach;
throw_if_esp_fail(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, (void *) this));
throw_if_esp_fail(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, ppp_netif));
throw_if_esp_fail(
esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected, ppp_netif));
esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected, ppp_netif));
throw_if_esp_fail(esp_netif_attach(ppp_netif, &driver));
}
void Netif::start() {
void Netif::start()
{
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
receive(data, len);
return false;
@ -92,12 +99,14 @@ void Netif::start() {
signal.set(PPP_STARTED);
}
void Netif::stop() {
void Netif::stop()
{
esp_netif_action_stop(driver.base.netif, nullptr, 0, nullptr);
signal.clear(PPP_STARTED);
}
Netif::~Netif() {
Netif::~Netif()
{
if (signal.is_any(PPP_STARTED)) {
esp_netif_action_stop(driver.base.netif, nullptr, 0, nullptr);
signal.clear(PPP_STARTED);
@ -108,7 +117,8 @@ Netif::~Netif() {
esp_event_handler_unregister(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected);
}
void Netif::wait_until_ppp_exits() {
void Netif::wait_until_ppp_exits()
{
signal.wait(PPP_EXIT, 30000);
}

View File

@ -19,7 +19,8 @@
namespace esp_modem {
void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
int32_t event_id, void *event_data)
{
}
esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len)
@ -29,7 +30,8 @@ esp_err_t Netif::esp_modem_dte_transmit(void *h, void *buffer, size_t len)
return len;
}
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args) {
esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
{
return ESP_OK;
}
@ -39,7 +41,7 @@ void Netif::receive(uint8_t *data, size_t len)
}
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
ppp_dte(std::move(e)), netif(ppp_netif) {}
ppp_dte(std::move(e)), netif(ppp_netif) {}
void Netif::start()
{
@ -48,7 +50,7 @@ void Netif::start()
return false;
});
netif->transmit = esp_modem_dte_transmit;
netif->ctx = (void*)this;
netif->ctx = (void *)this;
signal.set(PPP_STARTED);
}
@ -56,7 +58,8 @@ void Netif::stop() {}
Netif::~Netif() = default;
void Netif::wait_until_ppp_exits() {
void Netif::wait_until_ppp_exits()
{
}

View File

@ -20,7 +20,8 @@
namespace esp_modem {
void Lock::unlock() {
void Lock::unlock()
{
xSemaphoreGiveRecursive(m);
}
@ -30,11 +31,13 @@ Lock::Lock(): m(nullptr)
throw_if_false(m != nullptr, "create signal event group failed");
}
Lock::~Lock() {
Lock::~Lock()
{
vSemaphoreDelete(m);
}
void Lock::lock() {
void Lock::lock()
{
xSemaphoreTakeRecursive(m, portMAX_DELAY);
}
@ -74,12 +77,13 @@ bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
SignalGroup::~SignalGroup()
{
if (event_group)
if (event_group) {
vEventGroupDelete(event_group);
}
}
Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function)
:task_handle(nullptr)
: task_handle(nullptr)
{
BaseType_t ret = xTaskCreate(task_function, "vfs_task", stack_size, task_param, priority, &task_handle);
throw_if_false(ret == pdTRUE, "create vfs task failed");
@ -87,8 +91,9 @@ Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t
Task::~Task()
{
if (task_handle)
if (task_handle) {
vTaskDelete(task_handle);
}
}
void Task::Delete()
@ -101,4 +106,9 @@ void Task::Relinquish()
vTaskDelay(1);
}
} // namespace esp_modem
void Task::Delay(uint32_t ms)
{
vTaskDelay(pdMS_TO_TICKS(ms));
}
} // namespace esp_modem

View File

@ -46,8 +46,9 @@ void SignalGroup::clear(uint32_t bits)
bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{
if ((flags&event_group->flags) == flags) {
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] {
if ((flags & event_group->flags) == flags)
{
event_group->flags &= ~flags;
return true;
}
@ -60,13 +61,13 @@ bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
bool SignalGroup::is_any(uint32_t flags)
{
std::unique_lock<std::mutex> lock(event_group->m);
return flags&event_group->flags;
return flags & event_group->flags;
}
bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ return flags&event_group->flags; });
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; });
}
SignalGroup::~SignalGroup() = default;
@ -88,4 +89,9 @@ void Task::Relinquish()
usleep(0);
}
} // namespace esp_modem
void Task::Delay(uint32_t ms)
{
usleep(ms*1000);
}
} // namespace esp_modem

View File

@ -29,7 +29,8 @@ struct File {
fd(config->vfs_config.fd), deleter(config->vfs_config.deleter), resource(config->vfs_config.resource)
{}
~File() {
~File()
{
if (deleter) {
deleter(fd, resource);
}
@ -45,11 +46,13 @@ public:
~FdTerminal() override;
void start() override {
void start() override
{
signal.set(TASK_START);
}
void stop() override {
void stop() override
{
signal.clear(TASK_START);
}
@ -57,8 +60,9 @@ public:
int read(uint8_t *data, size_t len) override;
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
on_data = std::move(f);
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
{
on_read = std::move(f);
signal.set(TASK_PARAMS);
}
@ -75,26 +79,28 @@ private:
Task task_handle;
};
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config) {
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config)
{
TRY_CATCH_RET_NULL(
auto term = std::make_unique<FdTerminal>(config);
term->start();
return term;
auto term = std::make_unique<FdTerminal>(config);
term->start();
return term;
)
}
FdTerminal::FdTerminal(const esp_modem_dte_config *config) :
f(config), signal(),
task_handle(config->task_stack_size, config->task_priority, this, [](void* p){
auto t = static_cast<FdTerminal *>(p);
t->task();
Task::Delete();
})
{}
f(config), signal(),
task_handle(config->task_stack_size, config->task_priority, this, [](void *p)
{
auto t = static_cast<FdTerminal *>(p);
t->task();
Task::Delete();
})
{}
void FdTerminal::task()
{
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
std::function<bool(uint8_t *data, size_t len)> on_read_priv = nullptr;
signal.set(TASK_INIT);
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
if (signal.is_any(TASK_STOP)) {
@ -105,15 +111,15 @@ void FdTerminal::task()
int s;
fd_set rfds;
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
.tv_sec = 1,
.tv_usec = 0,
};
FD_ZERO(&rfds);
FD_SET(f.fd, &rfds);
s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv);
if (signal.is_any(TASK_PARAMS)) {
on_data_priv = on_data;
on_read_priv = on_read;
signal.clear(TASK_PARAMS);
}
@ -123,9 +129,9 @@ void FdTerminal::task()
// ESP_LOGV(TAG, "Select exited with timeout");
} else {
if (FD_ISSET(f.fd, &rfds)) {
if (on_data_priv) {
if (on_data_priv(nullptr, 0)) {
on_data_priv = nullptr;
if (on_read_priv) {
if (on_read_priv(nullptr, 0)) {
on_read_priv = nullptr;
}
}
}

View File

@ -25,8 +25,8 @@ uart_resource::~uart_resource()
}
}
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t* event_queue, int fd)
:port(-1)
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t *event_queue, int fd)
: port(-1)
{
esp_err_t res;
@ -37,7 +37,7 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
uart_config.parity = config->parity;
uart_config.stop_bits = config->stop_bits;
uart_config.flow_ctrl = (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS
: UART_HW_FLOWCTRL_DISABLE;
: UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_APB;
throw_if_esp_fail(uart_param_config(config->port_num, &uart_config), "config uart parameter failed");

View File

@ -30,13 +30,17 @@ namespace esp_modem {
struct uart_task {
explicit uart_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) :
task_handle(nullptr) {
task_handle(nullptr)
{
BaseType_t ret = xTaskCreate(task_function, "uart_task", stack_size, task_param, priority, &task_handle);
throw_if_false(ret == pdTRUE, "create uart event task failed");
}
~uart_task() {
if (task_handle) vTaskDelete(task_handle);
~uart_task()
{
if (task_handle) {
vTaskDelete(task_handle);
}
}
TaskHandle_t task_handle; /*!< UART event task handle */
@ -47,16 +51,18 @@ struct uart_task {
class UartTerminal : public Terminal {
public:
explicit UartTerminal(const esp_modem_dte_config *config) :
event_queue(), uart(&config->uart_config, &event_queue, -1), signal(),
task_handle(config->task_stack_size, config->task_priority, this, s_task) {}
event_queue(), uart(&config->uart_config, &event_queue, -1), signal(),
task_handle(config->task_stack_size, config->task_priority, this, s_task) {}
~UartTerminal() override = default;
void start() override {
void start() override
{
signal.set(TASK_START);
}
void stop() override {
void stop() override
{
signal.set(TASK_STOP);
}
@ -64,24 +70,28 @@ public:
int read(uint8_t *data, size_t len) override;
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override {
on_data = std::move(f);
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
{
on_read = std::move(f);
signal.set(TASK_PARAMS);
}
private:
static void s_task(void *task_param) {
static void s_task(void *task_param)
{
auto t = static_cast<UartTerminal *>(task_param);
t->task();
vTaskDelete(nullptr);
}
void task();
bool get_event(uart_event_t &event, uint32_t time_ms) {
bool get_event(uart_event_t &event, uint32_t time_ms)
{
return xQueueReceive(event_queue, &event, pdMS_TO_TICKS(time_ms));
}
void reset_events() {
void reset_events()
{
uart_flush_input(uart.port);
xQueueReset(event_queue);
}
@ -97,16 +107,18 @@ private:
uart_task task_handle;
};
std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *config) {
std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *config)
{
TRY_CATCH_RET_NULL(
auto term = std::make_unique<UartTerminal>(config);
term->start();
return term;
auto term = std::make_unique<UartTerminal>(config);
term->start();
return term;
)
}
void UartTerminal::task() {
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
void UartTerminal::task()
{
std::function<bool(uint8_t *data, size_t len)> on_read_priv = nullptr;
uart_event_t event;
size_t len;
signal.set(TASK_INIT);
@ -117,48 +129,53 @@ void UartTerminal::task() {
while (signal.is_any(TASK_START)) {
if (get_event(event, 100)) {
if (signal.is_any(TASK_PARAMS)) {
on_data_priv = on_data;
on_read_priv = on_read;
signal.clear(TASK_PARAMS);
}
switch (event.type) {
case UART_DATA:
uart_get_buffered_data_len(uart.port, &len);
if (len && on_data_priv) {
if (on_data_priv(nullptr, len)) {
on_data_priv = nullptr;
}
case UART_DATA:
uart_get_buffered_data_len(uart.port, &len);
if (len && on_read_priv) {
if (on_read_priv(nullptr, len)) {
on_read_priv = nullptr;
}
break;
case UART_FIFO_OVF:
ESP_LOGW(TAG, "HW FIFO Overflow");
if (on_error)
on_error(terminal_error::BUFFER_OVERFLOW);
reset_events();
break;
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "Ring Buffer Full");
if (on_error)
on_error(terminal_error::BUFFER_OVERFLOW);
reset_events();
break;
case UART_BREAK:
ESP_LOGW(TAG, "Rx Break");
if (on_error)
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
break;
case UART_PARITY_ERR:
ESP_LOGE(TAG, "Parity Error");
if (on_error)
on_error(terminal_error::CHECKSUM_ERROR);
break;
case UART_FRAME_ERR:
ESP_LOGE(TAG, "Frame Error");
if (on_error)
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
break;
default:
ESP_LOGW(TAG, "unknown uart event type: %d", event.type);
break;
}
break;
case UART_FIFO_OVF:
ESP_LOGW(TAG, "HW FIFO Overflow");
if (on_error) {
on_error(terminal_error::BUFFER_OVERFLOW);
}
reset_events();
break;
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "Ring Buffer Full");
if (on_error) {
on_error(terminal_error::BUFFER_OVERFLOW);
}
reset_events();
break;
case UART_BREAK:
ESP_LOGW(TAG, "Rx Break");
if (on_error) {
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
}
break;
case UART_PARITY_ERR:
ESP_LOGE(TAG, "Parity Error");
if (on_error) {
on_error(terminal_error::CHECKSUM_ERROR);
}
break;
case UART_FRAME_ERR:
ESP_LOGE(TAG, "Frame Error");
if (on_error) {
on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
}
break;
default:
ESP_LOGW(TAG, "unknown uart event type: %d", event.type);
break;
}
}
}

View File

@ -23,7 +23,7 @@ namespace esp_modem {
constexpr const char *TAG = "uart_resource";
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t* event_queue, int fd): port(-1)
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t *event_queue, int fd): port(-1)
{
ESP_LOGD(TAG, "Creating uart resource" );
struct termios tty = {};
@ -39,7 +39,7 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 0;

View File

@ -34,7 +34,7 @@ constexpr const char *TAG = "vfs_socket_creator";
* @note: Remote command:
* socat TCP-L:2222 GOPEN:/dev/ttyS0,ispeed=115200,ospeed=1152000,b115200,raw,echo=0
*/
static esp_err_t hostname_to_fd(const char *host, int port, int* fd)
static esp_err_t hostname_to_fd(const char *host, int port, int *fd)
{
struct sockaddr_storage address = {};
struct addrinfo *address_info;
@ -46,7 +46,7 @@ static esp_err_t hostname_to_fd(const char *host, int port, int* fd)
int res = getaddrinfo(host, nullptr, &hints, &address_info);
if (res != 0 || address_info == nullptr) {
ESP_LOGE(TAG, "couldn't get hostname for :%s: "
"getaddrinfo() returns %d, addrinfo=%p", host, res, address_info);
"getaddrinfo() returns %d, addrinfo=%p", host, res, address_info);
return ESP_FAIL;
}
*fd = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
@ -77,7 +77,7 @@ static esp_err_t hostname_to_fd(const char *host, int port, int* fd)
}
static void vfs_destroy_socket(int fd, struct esp_modem_vfs_resource * resource)
static void vfs_destroy_socket(int fd, struct esp_modem_vfs_resource *resource)
{
if (fd >= 0) {
close(fd);

View File

@ -27,13 +27,13 @@ constexpr const char *TAG = "vfs_uart_creator";
struct esp_modem_vfs_resource {
explicit esp_modem_vfs_resource(const esp_modem_uart_term_config *config, int fd)
: internal(config, nullptr, fd) {}
: internal(config, nullptr, fd) {}
esp_modem::uart_resource internal;
};
static void vfs_destroy_uart(int fd, struct esp_modem_vfs_resource * resource)
static void vfs_destroy_uart(int fd, struct esp_modem_vfs_resource *resource)
{
if (fd >= 0) {
close(fd);
@ -58,7 +58,7 @@ bool vfs_create_uart(struct esp_modem_vfs_uart_creator *config, struct esp_modem
int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
, return false)
, return false)
return true;
}

View File

@ -15,8 +15,8 @@ void LoopbackTerm::stop()
int LoopbackTerm::write(uint8_t *data, size_t len)
{
if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder
std::string command((char*)data, len);
if (len > 2 && (data[len - 1] == '\r' || data[len - 1] == '+') ) { // Simple AT responder
std::string command((char *)data, len);
std::string response;
if (command == "+++") {
response = "NO CARRIER\r\n";
@ -28,14 +28,16 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
response = "CONNECT\r\n";
} else if (command.find("AT+CSQ\r") != std::string::npos) {
response = "+CSQ: 123,456\n\r\nOK\r\n";
} else if (command.find("AT+CGMM\r") != std::string::npos) {
response = "0G Dummy Model\n\r\nOK\r\n";
} else if (command.find("AT+CBC\r") != std::string::npos) {
response = is_bg96 ? "+CBC: 1,2,123456V\r\r\n\r\nOK\r\n\n\r\n":
"+CBC: 123.456V\r\r\n\r\nOK\r\n\n\r\n";
response = is_bg96 ? "+CBC: 1,20,123456\r\r\n\r\nOK\r\n\n\r\n" :
"+CBC: 123.456V\r\r\n\r\nOK\r\n\n\r\n";
} else if (command.find("AT+CPIN=1234\r") != std::string::npos) {
response = "OK\r\n";
pin_ok = true;
} else if (command.find("AT+CPIN?\r") != std::string::npos) {
response = pin_ok?"+CPIN: READY\r\nOK\r\n":"+CPIN: SIM PIN\r\nOK\r\n";
response = pin_ok ? "+CPIN: READY\r\nOK\r\n" : "+CPIN: SIM PIN\r\nOK\r\n";
} else if (command.find("AT") != std::string::npos) {
response = "OK\r\n";
}
@ -43,22 +45,22 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
data_len = response.length();
loopback_data.resize(data_len);
memcpy(&loopback_data[0], &response[0], data_len);
auto ret = std::async(on_data, nullptr, data_len);
auto ret = std::async(on_read, nullptr, data_len);
return len;
}
}
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
// turn the request into a reply -> implements CMUX loopback
if (data[2] == 0x3f) // SABM command
if (data[2] == 0x3f) { // SABM command
data[2] = 0x73;
else if (data[2] == 0xef) { // Generic request
} else if (data[2] == 0xef) { // Generic request
data[2] = 0xff; // generic reply
}
}
loopback_data.resize(data_len + len);
memcpy(&loopback_data[data_len], data, len);
data_len += len;
auto ret = std::async(on_data, nullptr, data_len);
auto ret = std::async(on_read, nullptr, data_len);
return len;
}
@ -66,8 +68,9 @@ int LoopbackTerm::read(uint8_t *data, size_t len)
{
size_t read_len = std::min(data_len, len);
if (read_len) {
if (loopback_data.capacity() < len)
if (loopback_data.capacity() < len) {
loopback_data.reserve(len);
}
memcpy(data, &loopback_data[0], read_len);
loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len);
data_len -= read_len;

View File

@ -1,5 +1,4 @@
#ifndef _LOOPBACKTERM_H_
#define _LOOPBACKTERM_H_
#pragma once
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_terminal.hpp"
@ -32,5 +31,3 @@ private:
bool pin_ok;
bool is_bg96;
};
#endif //_LOOPBACKTERM_H_

View File

@ -24,7 +24,7 @@ TEST_CASE("DCE AT parser", "[esp_modem]")
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
CHECK(milli_volt == 123456);
CHECK(bcl == 1);
CHECK(bcs == 2);
CHECK(bcs == 20);
int rssi, ber;
CHECK(dce->get_signal_quality(rssi, ber) == command_result::OK);
@ -37,6 +37,10 @@ TEST_CASE("DCE AT parser", "[esp_modem]")
CHECK(dce->set_pin("1234") == command_result::OK);
CHECK(dce->read_pin(pin_ok) == command_result::OK);
CHECK(pin_ok == true);
std::string model;
CHECK(dce->get_module_name(model) == command_result::OK);
CHECK(model == "0G Dummy Model");
}
@ -51,7 +55,7 @@ TEST_CASE("DTE send/receive command", "[esp_modem]")
CHECK(dte->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
auto ret = dte->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char*)data, len);
std::string response((char *)data, len);
CHECK(response == test_command);
return command_result::OK;
}, 1000);
@ -71,7 +75,7 @@ TEST_CASE("DCE commands", "[esp_modem]")
const auto test_command = "Test\n";
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char*)data, len);
std::string response((char *)data, len);
CHECK(response == test_command);
return command_result::OK;
}, 1000);
@ -114,7 +118,8 @@ TEST_CASE("DCE modes", "[esp_modem]")
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("DCE CMUX test", "[esp_modem]") {
TEST_CASE("DCE CMUX test", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);

View File

@ -26,7 +26,7 @@ typedef DCE_T<NetModule> NetDCE;
class NetDCE_Factory: public Factory {
public:
template <typename T, typename ...Args>
static DCE_T<T>* create(const config *cfg, Args&&... args)
static DCE_T<T> *create(const config *cfg, Args &&... args)
{
return build_generic_DCE<T>(cfg, std::forward<Args>(args)...);
}
@ -38,7 +38,7 @@ public:
class NetModule: public ModuleIf {
public:
explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg):
dte(std::move(dte)) {}
dte(std::move(dte)) {}
bool setup_data_mode() override
{
@ -62,9 +62,18 @@ public:
return dce == nullptr ? ESP_FAIL : ESP_OK;
}
static void deinit() { delete dce; }
static void start() { dce->set_data(); }
static void stop() { dce->exit_data(); }
static void deinit()
{
delete dce;
}
static void start()
{
dce->set_data();
}
static void stop()
{
dce->exit_data();
}
private:
static NetDCE *dce;

View File

@ -120,15 +120,15 @@ TEST_CASE("Disconnection test", "[esp_modem]")
extern "C" {
static void handle(int nr)
{
ESP_LOGE(TAG, "Signal handler %d", nr);
}
static void handle(int nr)
{
ESP_LOGE(TAG, "Signal handler %d", nr);
}
_sig_func_ptr signal (int nr, _sig_func_ptr)
{
return handle;
}
_sig_func_ptr signal (int nr, _sig_func_ptr)
{
return handle;
}
}

View File

@ -0,0 +1,93 @@
.config
*.o
*.pyc
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Components Unit Test Apps files
components/**/build
components/**/sdkconfig
components/**/sdkconfig.old
# Example project files
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build
# Doc build artifacts
docs/_build/
docs/doxygen_sqlite3.db
# Downloaded font files
docs/_static/DejaVuSans.ttf
docs/_static/NotoSansSC-Regular.otf
# Unit test app files
tools/unit-test-app/sdkconfig
tools/unit-test-app/sdkconfig.old
tools/unit-test-app/build
tools/unit-test-app/builds
tools/unit-test-app/output
tools/unit-test-app/test_configs
# Unit Test CMake compile log folder
log_ut_cmake
# test application build files
test/**/build
test/**/sdkconfig
test/**/sdkconfig.old
# IDF monitor test
tools/test_idf_monitor/outputs
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
test_multi_heap_host
# VS Code Settings
.vscode/
# VIM files
*.swp
*.swo
# Clion IDE CMake build & config
.idea/
cmake-build-*/
# Results for the checking of the Python coding style and static analysis
.mypy_cache
flake8_output.txt
# ESP-IDF default build directory name
build
# lock files for examples and components
dependencies.lock
# ignore generated docs
docs/html

View File

@ -0,0 +1,12 @@
if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION)
message(STATUS "Websocket transport is disabled so the esp_websocket_client component will not be built")
# note: the component is still included in the build so it can become visible again in config
# without needing to re-run CMake. However no source or header files are built.
idf_component_register()
return()
endif()
idf_component_register(SRCS "esp_websocket_client.c"
INCLUDE_DIRS "include"
REQUIRES lwip esp-tls tcp_transport http_parser
PRIV_REQUIRES esp_timer)

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,11 @@
# ESP WEBSOCKET CLIENT
The `esp-websocket_client` component is a managed component for `esp-idf` that contains implementation of [WebSocket protocol client](https://datatracker.ietf.org/doc/html/rfc6455) for ESP32
## Examples
Get started with example test [Example](examples/README.md):
## Documentation
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)

View File

@ -0,0 +1,75 @@
# This is Doxygen configuration file
#
# Doxygen provides over 260 configuration statements
# To make this file easier to follow,
# it contains only statements that are non-default
#
# NOTE:
# It is recommended not to change defaults unless specifically required
# Test any changes how they affect generated documentation
# Make sure that correct warnings are generated to flag issues with documented code
#
# For the complete list of configuration statements see:
# http://doxygen.nl/manual/config.html
PROJECT_NAME = "ESP Protocols Programming Guide"
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
## to automatically generate API reference list files heder_file.inc
## These files are placed in '_inc' directory
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/include/esp_websocket_client.h
## Get warnings for functions that have no documentation for their parameters or return value
##
WARN_NO_PARAMDOC = YES
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
##
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = \
$(ENV_DOXYGEN_DEFINES) \
__DOXYGEN__=1 \
__attribute__(x)= \
_Static_assert()= \
IDF_DEPRECATED(X)= \
IRAM_ATTR= \
configSUPPORT_DYNAMIC_ALLOCATION=1 \
configSUPPORT_STATIC_ALLOCATION=1 \
configQUEUE_REGISTRY_SIZE=1 \
configUSE_RECURSIVE_MUTEXES=1 \
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
configUSE_APPLICATION_TASK_TAG=1 \
configTASKLIST_INCLUDE_COREID=1 \
"ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x"
## Do not complain about not having dot
##
HAVE_DOT = NO
## Generate XML that is required for Breathe
##
GENERATE_XML = YES
XML_OUTPUT = xml
GENERATE_HTML = NO
HAVE_DOT = NO
GENERATE_LATEX = NO
GENERATE_MAN = YES
GENERATE_RTF = NO
## Skip distracting progress messages
##
QUIET = YES
## Enable Section Tags for conditional documentation
##
ENABLED_SECTIONS += \
DOC_EXCLUDE_HEADER_SECTION \ ## To conditionally remove doc sections from IDF source files without affecting documentation in upstream files.
DOC_SINGLE_GROUP ## To conditionally remove groups from the documentation and create a 'flat' document without affecting documentation in upstream files.

Some files were not shown because too many files have changed in this diff Show More