Compare commits

...

417 Commits

Author SHA1 Message Date
5addf9e885 feat(esp_modem): Bumped version number to 0.1.17 2022-06-10 14:56:12 +02:00
128c0a2d87 fix(esp_modem): Support 2 byte size packets
Closes https://github.com/espressif/esp-protocols/issues/46
2022-06-06 15:05:38 +02:00
1f91d248f5 Merge pull request #51 from gabsuren/bugfix/mix_fixes
Minor fixes here and there
2022-06-03 15:49:26 +02:00
8fe2a3a661 Minor fixes here and there
1. mDNS: Added idf_component.yml
2. mDNS: Updated document generation paths
2. Websocket: Updated conf_common.py to use internal
3. Websocket: Updated CmakeList.txt to use internal
2022-06-03 17:18:57 +04:00
417591b99f Merge pull request #43 from gabsuren/mdns_migration_with_history
mdns: Initial version based on IDF 5.0
2022-06-03 12:57:35 +02:00
b6b20ad399 mDNS: Initial version based on IDF 5.0 2022-06-01 21:04:37 +04:00
8863ed944d docs: update redirected links
* Original commit: espressif/esp-idf@030cb77597
2022-05-27 17:44:25 +04:00
78001ec871 Examples: common source for GPIO range in Kconfigs defined
* Original commit: espressif/esp-idf@1a20b10fd3
2022-05-27 17:44:25 +04:00
4a52cf23f5 soc: moved kconfig options out of the target component.
Moved the following kconfig options out of the target component:
 * CONFIG_ESP*_DEFAULT_CPU_FREQ* -> esp_system
 * ESP*_REV_MIN -> esp_hw_support
 * ESP*_TIME_SYSCALL -> newlib
 * ESP*_RTC_* -> esp_hw_support

Where applicable these target specific konfig names were merged into
a single common config, e.g;
CONFIG_ESP*_DEFAULT_CPU_FREQ -> CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ


* Original commit: espressif/esp-idf@d2872095f9
2022-05-27 17:44:25 +04:00
00e7675913 examples: added ESP-NETIF L2 TAP example
* Original commit: espressif/esp-idf@fcdb0306d0
2022-05-27 17:44:25 +04:00
87c269911d cmake: fix issue with passing cxx_std option for GCC 11, a common workaround
* Original commit: espressif/esp-idf@ea0d2123c8
2022-05-27 17:44:25 +04:00
eb536a74a0 kconfig: Changed default values of bool configs
- Some bool configs were using default values true and false,
  instead of y and n.


* Original commit: espressif/esp-idf@25c5c214f3
2022-05-27 17:44:25 +04:00
b720d02fca examples: Fix implicit includes after legacy code removal
* Original commit: espressif/esp-idf@c941e29cf6
2022-05-27 17:44:25 +04:00
3e93ea9b76 esp_netif: Remove tcpip_adapter compatibility layer
* Original commit: espressif/esp-idf@795b7ed993
2022-05-27 17:44:25 +04:00
2c764b1b7a mdns: Fix copyright messages, update API descrition
* Impove docs and comments on custom netifs
* Make predef interfaces const, minor docs fixes


* Original commit: espressif/esp-idf@42ba8a8338
2022-05-27 17:44:25 +04:00
f836ae7f65 mdns: Add API to control custom network interfaces
* Original commit: espressif/esp-idf@b02468dc98
2022-05-27 17:44:25 +04:00
e9a1c26e0c CI/mdns: Reworked example test to be run repeatably
* Original commit: espressif/esp-idf@dd3cd52fd6
2022-05-27 17:44:25 +04:00
4b5f24f5b8 CI/mdns: Fix fuzzer build
* Original commit: espressif/esp-idf@98e9426b66
2022-05-27 17:44:25 +04:00
05675c7d63 CI/mdns: Extend example test for sockets, netifs
* Original commit: espressif/esp-idf@d1b809e6a1
2022-05-27 17:44:25 +04:00
30f37c0565 mdns: Add support for registering custom netif
* Original commit: espressif/esp-idf@bec42ff85d
2022-05-27 17:44:25 +04:00
ddc58e8220 mdns: Indicate interface using esp_netif in search results
* Original commit: espressif/esp-idf@f8495f1e86
2022-05-27 17:44:25 +04:00
fa951bf320 mdns: Use predefined interfaces to prepare for custom netifs
* Original commit: espressif/esp-idf@f90b3b798b
2022-05-27 17:44:25 +04:00
605d1fab73 mdns: Prepare for dynamic esp-netif support
* Original commit: espressif/esp-idf@f9892f77b8
2022-05-27 17:44:25 +04:00
58bf2186f7 esp_hw_support/esp_system: Re-evaluate header inclusions and include directories
This commit updates the visibility of various header files and cleans up
some unnecessary inclusions. Also, this commit removes certain header
include paths which were maintained for backward compatibility.


* Original commit: espressif/esp-idf@a9fda54d39
2022-05-27 17:44:24 +04:00
d39a4c744e mdns: Stabilization of mdns test app
* Original commit: espressif/esp-idf@93a902ba80
2022-05-27 17:44:24 +04:00
ec491ec536 system: move kconfig options out of target component
Moved the following kconfig options out of the target component:
 * ESP32_X_BROWNOUT_* -> esp_system
 * ESP32_X_DEBUG_OCDAWARE -> esp_system
 * APP_NO_BLOBS -> build type (main kconfig)


* Original commit: espressif/esp-idf@bb88338118
2022-05-27 17:44:24 +04:00
defdfd59b6 G0: target component (components/esp32*) doesn't depend on driver anymore
* Original commit: espressif/esp-idf@2571aaf3c9
2022-05-27 17:44:24 +04:00
7f42c31252 ci/mdsn: Fix example test on ethernet runners
* Ethernet kit uses GPIO0 for ref-clock, so the test button hits
constantly
* Add a freeRTOS delay when checking result on assync queries


* Original commit: espressif/esp-idf@afe7ab3b2c
2022-05-27 17:44:24 +04:00
94ae672041 mdns: Update to drop our own packet if bounced back
* Original commit: espressif/esp-idf@b5149e3ee7
2022-05-27 17:44:24 +04:00
e5a3a3df1d mdns: Fix potential read behind parsed packet
* Original commit: espressif/esp-idf@51a5de2525
2022-05-27 17:44:24 +04:00
7710ea9a11 mdns: Fix memleak when adding delegated host
* Original commit: espressif/esp-idf@9cbdb8767b
2022-05-27 17:44:24 +04:00
034c55e18a mdns: Fix null-service issue when parsing packets
Closes https://github.com/espressif/esp-idf/issues/8307


* Original commit: espressif/esp-idf@a57be7b7d1
2022-05-27 17:44:24 +04:00
ec03fec3d3 mdns: Update fuzzer test (add delegation, check memory)
* Add new config with no services
* Add new test packets and more queries
* Allocate packet to check for mem issues


* Original commit: espressif/esp-idf@2c1007156e
2022-05-27 17:44:24 +04:00
5909e9e54c mdns: Remove legacy esp_event API
* Original commit: espressif/esp-idf@e46aa515bd
2022-05-27 17:44:24 +04:00
82e2a5dcc1 mdns: added missing includes
* Original commit: espressif/esp-idf@28d09c7dbe
2022-05-27 17:44:24 +04:00
48e4d4035c mdns: Clear notification value in mdns_hostname_set
Merges https://github.com/espressif/esp-idf/pull/8284


* Original commit: espressif/esp-idf@83a4ddbd25
2022-05-27 17:44:24 +04:00
ba3fa24a96 esp_eth: Update esp32's EMAC API to decouple driver and vendor config
* Original commit: espressif/esp-idf@8da2e4088c
2022-05-27 17:44:24 +04:00
869f5b75af esp_eth: Make EMAC DMA burst size configurable
Merges https://github.com/espressif/esp-idf/pull/7874
Closes  https://github.com/espressif/esp-idf/issues/7380


* Original commit: espressif/esp-idf@2553fb5845
2022-05-27 17:44:24 +04:00
ac6dcb6830 esp_timer: remove legacy ESP32 FRC timer implementation.
* Original commit: espressif/esp-idf@edb76f14d6
2022-05-27 17:44:24 +04:00
085dbd8c4e 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-05-27 17:44:24 +04:00
f78e8cfce8 Tools: Custom baud-rate setup is not possible for IDF Monitor from menuconfig anymore
IDF Monitor follows the console baud rate by default. Other baud rate
can be set from command line by "idf.py monitor -B <baud>" or through
environment variables. Run "idf.py monitor --help" for more information.


* Original commit: espressif/esp-idf@36a4011ff8
2022-05-27 17:44:24 +04:00
6cdf5ee074 mdns: Use memcpy() for copy to support non-text TXTs
* Original commit: espressif/esp-idf@6aefe9c185
2022-05-27 17:44:24 +04:00
fcb5515f1e mdns: Support for null-value TXT records
Closes https://github.com/espressif/esp-idf/issues/8267


* Original commit: espressif/esp-idf@23c2db406d
2022-05-27 17:44:24 +04:00
9fdbe5f130 mdns: Fix alloc issue if TXT has empty value
* Original commit: espressif/esp-idf@205f6ba854
2022-05-27 17:44:24 +04:00
20e6e9e7fe mdns: Fix random crash when defalt service instance queried
Merges https://github.com/espressif/esp-idf/pull/8248


* Original commit: espressif/esp-idf@f46dffca62
2022-05-27 17:44:24 +04:00
3c5b13ea0d ci/mdns: Run mdns test on ethernet runners
* Original commit: espressif/esp-idf@96616b6056
2022-05-27 17:44:24 +04:00
c588263b45 mdns: Fix minor memory leaks when creating services
* Original commit: espressif/esp-idf@fad62cc1ed
2022-05-27 17:44:24 +04:00
6258edf23b Fix mDNS memory leak
* Original commit: espressif/esp-idf@119b4a9dd1
2022-05-27 17:44:24 +04:00
c8b0d5ea9d Fix mDNS memory leak
* Original commit: espressif/esp-idf@f5ffd53aeb
2022-05-27 17:44:24 +04:00
5252b1d801 mdns: Use multi/uni-cast types in API
* Original commit: espressif/esp-idf@125c312552
2022-05-27 17:44:24 +04:00
4e11cc86fe mdns: Allow for unicast PTR queries
Adresses https://github.com/espressif/esp-idf/issues/7932


* Original commit: espressif/esp-idf@7eeeb01ea7
2022-05-27 17:44:24 +04:00
7af91ec490 mdns: Fix potential null deref for ANY query type
* Original commit: espressif/esp-idf@99dd8eedb1
2022-05-27 17:44:24 +04:00
01256d3e34 mdns: Make fuzzer layers compatible with llvm>=6
* Original commit: espressif/esp-idf@1882cbe44e
2022-05-27 17:44:24 +04:00
5a2d4eab6d mdns: Fix copyright
* Original commit: espressif/esp-idf@c83678f64f
2022-05-27 17:44:24 +04:00
9de3f534e2 Add mDNS miss comment
* Original commit: espressif/esp-idf@08e081340d
2022-05-27 17:44:24 +04:00
bcabc8ea16 freertos: remove FREERTOS_ASSERT option
Freertos asserts are now configured the same way as all other asserts in IDF,
i.e. by configuring COMPILER_OPTIMIZATION_ASSERTION_LEVEL.


* Original commit: espressif/esp-idf@7255497146
2022-05-27 17:44:24 +04:00
dfb27b39cc mdns: Minor err print fix in socket-networking layer
* Original commit: espressif/esp-idf@f1b8f5c102
2022-05-27 17:44:24 +04:00
076c095aec unified errno format
* Original commit: espressif/esp-idf@87506f46e2
2022-05-27 17:44:24 +04:00
7dd0bc1fff mdns: always send A/AAAA records in announcements
* Original commit: espressif/esp-idf@456f80b754
2022-05-27 17:44:24 +04:00
7e82a7cef7 mdns: filter instance name for ANY queries
The instance name in ANY quries was ignored. The MR fixes the issue.


* Original commit: espressif/esp-idf@5d0c47303d
2022-05-27 17:44:24 +04:00
ae381b779f mdns: Fix potential null deref reported by fuzzer test
* Original commit: espressif/esp-idf@cb5653fd94
2022-05-27 17:44:24 +04:00
d0f4e68c7a mdns: Unbreak test app cauased by async API change
Regression from 81d496ace9080fcd97df3cc7e9ce279531d67a48


* Original commit: espressif/esp-idf@c0d08faf91
2022-05-27 17:44:24 +04:00
941dc5c42f mdns: Minor fix of API description and API usage
* Original commit: espressif/esp-idf@c297301ecc
2022-05-27 17:44:24 +04:00
525c64915e Added results count to MDNS
* Original commit: espressif/esp-idf@f391d610e8
2022-05-27 17:44:24 +04:00
f0839d909b mdns: fix mdns server instance mismatch
* Original commit: espressif/esp-idf@6173dd7809
2022-05-27 17:44:24 +04:00
69902ea8e1 mdns: support multiple instance for mdns service txt set
* Original commit: espressif/esp-idf@50f6302c5d
2022-05-27 17:44:24 +04:00
d5001e894f mdns: added test app
Closes IDF-4132


* Original commit: espressif/esp-idf@e0d5fca390
2022-05-27 17:44:24 +04:00
145a8d2291 esp_eth: rework KSZ80xx implementation and add more KSZ80xx PHYs
* add support for KSZ8001, KSZ8021, KSZ8031, KSZ8051 and KSZ8061
* remove duplicate code
* simplify architecture to make the code base extensible (for future work)


* Original commit: espressif/esp-idf@3fb83f2866
2022-05-27 17:44:24 +04:00
d0bbe880b6 mdns: fix wrong PTR record count
* Original commit: espressif/esp-idf@5d3f8157e0
2022-05-27 17:44:24 +04:00
4a9d55edf7 Build & config: Remove leftover files from the unsupported "make" build system
* Original commit: espressif/esp-idf@766aa57084
2022-05-27 17:44:24 +04:00
be2a924674 Build & config: Remove the "make" build system
The "make" build system was deprecated in v4.0 in favor of idf.py
(cmake). The remaining support is removed in v5.0.


* Original commit: espressif/esp-idf@9c1d4f5b54
2022-05-27 17:44:24 +04:00
76fcd4128a freertos: update freertos folder structure to match upstream
The following changes have been made:
1. All FreeRTOS kernel source files are now placed in the
   freertos/FreeRTOS-Kernel folder to match with the upstream folder structure.
2. All kernel include files are now placed in freertos/FreeRTOS-Kernel/include.
3. All port files are now placed in freertos/FreeRTOS-Kernel/portable.
4. All additions/customizations are placed in freertos/esp_additions.
5. All other miscellaneous files (README, License files etc.) are moved to
   freertos/FreeRTOS-Kernel folder to match with the upstream.
6. Updated esp-cryptoauthlib to latest commit to resolve FreeRTOS
   include dependencies.

Signed-off-by: Sudeep Mohanty <sudeep.mohanty@espressif.com>


* Original commit: espressif/esp-idf@4846222102
2022-05-27 17:44:24 +04:00
fd8499c874 mdns: support service subtype
* Closes https://github.com/espressif/esp-idf/issues/5508


* Original commit: espressif/esp-idf@e7e8610f56
2022-05-27 17:44:24 +04:00
38b4fe2353 mdns: Fix parsing non-standard queries
Fix for packets containing unexpected domains, such as openthread.thread.home.arpa.
If we find this packet we set the name entry as invalid, but continue with parsing as the packet might contain related queries for us.

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


* Original commit: espressif/esp-idf@d16f9bade5
2022-05-27 17:44:24 +04:00
eb7eeb58d3 [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-05-27 17:44:24 +04:00
b26606252f mdns: allow mutiple instances with same service type
* Original commit: espressif/esp-idf@b7a99f4658
2022-05-27 17:44:24 +04:00
5e087d82d6 mdns: Update copyright header
* Original commit: espressif/esp-idf@2a2b95b9c2
2022-05-27 17:44:24 +04:00
91a3d95f96 mdns: Fix potential null dereference identified by fuzzer tests
* Original commit: espressif/esp-idf@e7dabb14f7
2022-05-27 17:44:24 +04:00
6d6dd2b75e components/bt: move config BT_RESERVE_DRAM from bluedroid to ESP32 controller
* Original commit: espressif/esp-idf@b310c062cd
2022-05-27 17:44:24 +04:00
0611b0cb67 Eth_examples: added support of ESP32-S3 chip
Defined SPI modules default GPIO values for ESP32-S3

SPI bus needs to be initialized with SPI_DMA_CH_AUTO option


* Original commit: espressif/esp-idf@35454b2bf7
2022-05-27 17:44:24 +04:00
cf6ed1cc53 esp_eth: add support for multiple Ethernets modules at a time
Ethernet driver events properly bounded with ESP NETIF actions to support multiple Ethernet modules used at a time.

Components using Ethernet updated to conform with new API.

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


* Original commit: espressif/esp-idf@ef30384902
2022-05-27 17:44:24 +04:00
52306e914f mdns: add notification callback for async APIs
* Original commit: espressif/esp-idf@986603cf07
2022-05-27 17:44:24 +04:00
d37ab6dd25 mdns: add more mdns result attributes
* Original commit: espressif/esp-idf@76ec76c12c
2022-05-27 17:44:24 +04:00
05dcd8f0ee examples/protocols: fix compilation when CONFIG_EXAMPLE_USE_OPENETH=y
The code checked CONFIG_ETH_USE_SPI_ETHERNET (which is usually set),
but CONFIG_EXAMPLE_ETH_SPI_xxx_GPIO options are only defined if
CONFIG_EXAMPLE_USE_SPI_ETHERNET is set. Fix the ifdef accordingly.
Regression from abc79de6.


* Original commit: espressif/esp-idf@ece73a3e55
2022-05-27 17:44:24 +04:00
5c55ea6e02 mdns: Add host test using linux target
* Original commit: espressif/esp-idf@fc7e2d9e90
2022-05-27 17:44:24 +04:00
0c71c7bfe1 mdns: Implement mdns_networking using BSD sockets
And use only if configured. By default we still use lwip raw (low-level) API


* Original commit: espressif/esp-idf@73dfe84bf2
2022-05-27 17:44:24 +04:00
4c368c0090 mdns: fix crash when adding services without hostname set
* Original commit: espressif/esp-idf@5e98772eaf
2022-05-27 17:44:24 +04:00
c2abeff476 examples: Update Ethernet examples to use new PHY LAN87xx init function
Ethernet examples device usage and Kconfig options synchronized


* Original commit: espressif/esp-idf@4e77430107
2022-05-27 17:44:24 +04:00
6a92d3253f CI: mdns example test: start responder after valid IP
to clean up the test and not to pollute the network unnecessarily.
Also keeps sending the delegated query until a response found


* Original commit: espressif/esp-idf@042fa1831e
2022-05-27 17:44:24 +04:00
af2275341e mdns: Fix fuzzer IDF-mock layer
Removed lwip dependencies
Simplified the mocks for esp32 and esp-netif


* Original commit: espressif/esp-idf@619235c2ee
2022-05-27 17:44:24 +04:00
b0957e70fd mdns: Clean the main mdns module from lwip dependencies
* Reduced number of include paths
* Abstract the internals of mdns packet (specifics defined in
mdns_networking.c)
* Use ESP_IP addresses instead of lwip addresses


* Original commit: espressif/esp-idf@54e329444a
2022-05-27 17:44:24 +04:00
47c7266103 mdns: Add asynchronous query API
Closes https://github.com/espressif/esp-idf/issues/7090


* Original commit: espressif/esp-idf@d81482d699
2022-05-27 17:44:24 +04:00
40da0d29be mdns: Fix crashes reported by the fuzzer tests
* Original commit: espressif/esp-idf@4a2e72677c
2022-05-27 17:44:24 +04:00
5f6b6f9273 mdns/fuzzer: Fix non-instrumentation test to reproduce fuzzer issues
Regression from 2893d7e21b skipped reading the packet causing issues when locally reproducing crashed found by the fuzzer


* Original commit: espressif/esp-idf@dae803335e
2022-05-27 17:44:24 +04:00
8a120829e2 mdns: return ESP_OK rather than ERR_OK in API functions
* Original commit: espressif/esp-idf@2386113972
2022-05-27 17:44:24 +04:00
46f28a8011 mdns: fix memory leak in mdns_free when adding delegated hostnames
* Original commit: espressif/esp-idf@0baee93211
2022-05-27 17:44:24 +04:00
5a81eaea3f mdns: Support for One-Shot mDNS queries
* Original commit: espressif/esp-idf@f167238fac
2022-05-27 17:44:24 +04:00
2ddaee2b6e mdns: allow explicit txt value length
* Original commit: espressif/esp-idf@b4e0088b68
2022-05-27 17:44:24 +04:00
73b1763029 example: set example wifi scan method to all channel:
in CI example test we could have runners with same SSID in the same lab.
Use scan on all channel will let DUT connect to the AP with best RSSI.


* Original commit: espressif/esp-idf@97a09e51ce
2022-05-27 17:44:24 +04:00
27fc285000 mdns: Fix crashes reported by the fuzzer
* Original commit: espressif/esp-idf@79ba738626
2022-05-27 17:44:24 +04:00
93e6efedc7 mdns: Minor correction of the test code
* Original commit: espressif/esp-idf@7d76245173
2022-05-27 17:44:24 +04:00
bc4cda8ea7 mdns: Fix fuzzer from miss-interpreting adding services as timeouts
* Original commit: espressif/esp-idf@14099fe15e
2022-05-27 17:44:24 +04:00
8a8d58d4dc mdns: fix test script delayed response
* Original commit: espressif/esp-idf@a4f263948c
2022-05-27 17:44:24 +04:00
402baebfee mdns: fix wrong SRV/PTR record handling
* Original commit: espressif/esp-idf@e6135552d2
2022-05-27 17:44:24 +04:00
9fa25ef3b6 mdns: fix wrong service hostname after mangling
* Original commit: espressif/esp-idf@439b31d065
2022-05-27 17:44:24 +04:00
121b525108 mdns: fix empty address change announce packets
* Original commit: espressif/esp-idf@7bbb72d865
2022-05-27 17:44:24 +04:00
418fb60dd9 mdns: fix mdns probe/reply behavior
* send correct hostnames when probing.
* add test for mdns host delegation.


* Original commit: espressif/esp-idf@d2a5d25984
2022-05-27 17:44:24 +04:00
4049b3b5ed mdns: make delegate host address a list
Also adds unit test and doc string for new apis.


* Original commit: espressif/esp-idf@2d34352f3d
2022-05-27 17:44:24 +04:00
c8821199a2 mdns: add remove delegate host api
* Original commit: espressif/esp-idf@2174693096
2022-05-27 17:44:24 +04:00
1eb5df9780 mdns: add mdns delegation
This allows publishing mdns services for other devices.


* Original commit: espressif/esp-idf@401ff56cc1
2022-05-27 17:44:24 +04:00
b62b4b3e25 mdns: fix memory free issue when repeating the query in reply
The repeated query will be copied in the next event loop while the
memory is freed instantly. Delay the free to fix this issue.


* Original commit: espressif/esp-idf@5f244c86f2
2022-05-27 17:44:24 +04:00
4d8aec1ad3 mdns: Fix of crash when wifi interface get deleted and mdns receives the packets
Closes https://github.com/espressif/esp-idf/issues/6973


* Original commit: espressif/esp-idf@03de74a728
2022-05-27 17:44:24 +04:00
6d649102ab Docs: Added README.md for lwip fuzzer tests
Closes IDFCI-540


* Original commit: espressif/esp-idf@53c18a85db
2022-05-27 17:44:24 +04:00
3ad559bf3f Split example_tests with Example_WIFI tag group into Example_OTA and Example_Protocols
* Original commit: espressif/esp-idf@0a395134d4
2022-05-27 17:44:24 +04:00
ab3fa69b8f mdns: Fixed the ip header TTL to be correctly set to 255
Defined in https://tools.ietf.org/html/rfc6762#section-11: All Multicast DNS responses (including responses sent via unicast)
   SHOULD be sent with IP TTL set to 255


* Original commit: espressif/esp-idf@5cce919cbe
2022-05-27 17:44:24 +04:00
c3a5826d60 mdns: Fix parsing answers with questions when instance name not set
mdns resolver didn't correctly resolved queries when host name wasn't
assigned. Fixed by allowing processing also if some answer present
(non-strict mode)

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


* Original commit: espressif/esp-idf@34049454df
2022-05-27 17:44:24 +04:00
cbcbe4ffd7 mdns: Fix the resolver to correctly parse it's own non-strict answers
The resolver was able to respond correctly, but would also resolve its
own queries and cause issues with BCT 1.5.2, specifically
* MULTIPLE QUESTIONS - DUPLICATE SUPPRESSION
* MULTIPLE QUESTIONS - DISTRIBUTED DUPLICATE SUPPRESSION
tests failed.


* Original commit: espressif/esp-idf@b649603a0d
2022-05-27 17:44:23 +04:00
adc34309dc mdns: Add MDNS_STRICT_MODE config option
Strict mode was hardcoded in private header file, but it's useful for
users to enable/disable it depending on the mdns library they are using.
e.g. Avahi might not resolve the non-strict answers.


* Original commit: espressif/esp-idf@0eee31546d
2022-05-27 17:44:23 +04:00
7a8329cb5c Bugfix: Connect example to add scan mode config
Closes https://github.com/espressif/esp-idf/issues/6595


* Original commit: espressif/esp-idf@3373eff989
2022-05-27 17:44:23 +04:00
c30617d872 freertos: common config header
* Original commit: espressif/esp-idf@39cf818838
2022-05-27 17:44:23 +04:00
1e5eeb16ec mdns: Removed freeRTOS dependancies from fuzzer tests
* Original commit: espressif/esp-idf@55716945a9
2022-05-27 17:44:23 +04:00
22c7c0a195 mDNS: Updated APIs description and shows the warning when hostname contains domain name during the query
Closes https://github.com/espressif/esp-idf/issues/6590


* Original commit: espressif/esp-idf@9f8d2b944d
2022-05-27 17:44:23 +04:00
eda5d72acf examples: Strip IPv6 function in example and use sockaddr_storage to replace sockaddr_in6
* Original commit: espressif/esp-idf@821eea45b3
2022-05-27 17:44:23 +04:00
1623c0e729 components: Use CONFIG_LWIP_IPV6 to strip IPv6 function in components
* Original commit: espressif/esp-idf@da58235a0e
2022-05-27 17:44:23 +04:00
b114ed69de mdns: add bound check when setting interface as duplicate
Closes IDF-2787

Partially addresses https://github.com/espressif/esp-idf/issues/6440


* Original commit: espressif/esp-idf@2b9d2c06f5
2022-05-27 17:44:23 +04:00
49a00c2455 style: format python files with isort and double-quote-string-fixer
* Original commit: espressif/esp-idf@0146f258d7
2022-05-27 17:44:23 +04:00
dec1d7dcf8 esp_wifi: Modify ESP_IF_WIFI_STA to WIFI_IF_STA
* Original commit: espressif/esp-idf@b8a8fe3f54
2022-05-27 17:44:23 +04:00
2ffd22382d mDNS: Fix of text length calculation when detecting a collision
* Original commit: espressif/esp-idf@be0ae1ebbb
2022-05-27 17:44:23 +04:00
487287157d example: We should not check the return value of esp_wifi_connect() in any case
* Original commit: espressif/esp-idf@c260c223e9
2022-05-27 17:44:23 +04:00
1fe901f70f global: fix sign-compare warnings
* Original commit: espressif/esp-idf@753a929525
2022-05-27 17:44:23 +04:00
2cf9fd8891 lwip: Moved default SNTP API to esp_sntp.h
and make sntp.h in port folders of lwip component obsoleted


* Original commit: espressif/esp-idf@76f6dd6214
2022-05-27 17:44:23 +04:00
89439e0a9b mdns: Allow resolve its own non-strict answers
the mDNS responder should not repeat questions when replying, however resolvers
must ignore these questions field if they are present. esp-idf mDNS
library does include questions in answering packets (thus not strictly
following the RFC6762) so the resolver did not correctly resolved
another instance host name.

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


* Original commit: espressif/esp-idf@0693e172de
2022-05-27 17:44:23 +04:00
becd5d0266 mDNS: Fix of collision detection during txt length calculation
Closes https://github.com/espressif/esp-idf/issues/6114


* Original commit: espressif/esp-idf@f33772c960
2022-05-27 17:44:23 +04:00
0d7a30944e esp32c3: Apply one-liner/small changes for ESP32-C3
* Original commit: espressif/esp-idf@5228d9f9ce
2022-05-27 17:44:23 +04:00
e2653e7fb0 eth: hide spi configuration when using internal emac
* Original commit: espressif/esp-idf@8d0a0537cc
2022-05-27 17:44:23 +04:00
df6a208f45 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-05-27 17:44:23 +04:00
1fdffbbbab test: fix several test build error
* Original commit: espressif/esp-idf@b7ecccd901
2022-05-27 17:44:23 +04:00
d5566971fb eth: support W5500 in network examples
* Original commit: espressif/esp-idf@aea901f014
2022-05-27 17:44:23 +04:00
988d120902 freertos: Add RISC-V port
Changes come from internal branch commit a6723fc


* Original commit: espressif/esp-idf@87e13baaf1
2022-05-27 17:44:23 +04:00
fd47df3e30 mdns: Fix wrong mdns source address if lwIP IPv6 zones disabled
The struct definition of ip6_addr_t in lwip and esp_ip6_addr_t
differs since zone could be possibly disabled in lwip. Using memcpy to copy the
address will cause wrong source address. Copy the entries manually
instead.

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


* Original commit: espressif/esp-idf@7ac97616c1
2022-05-27 17:44:23 +04:00
2cb3a6e35e 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-05-27 17:44:23 +04:00
ccd48bc9a9 lwip: Added description to Kconfig option on IPv6 SLAAC
Closes https://github.com/espressif/esp-idf/issues/6076
Merges https://github.com/espressif/esp-idf/pull/6078


* Original commit: espressif/esp-idf@9207c6ca8e
2022-05-27 17:44:23 +04:00
825652f3e1 test_compile_fuzzers: Fix include paths for host build
Regression in 988be6946681b592e3e51bb652b91bce54d7ba34, need to add
esp_hw_support component include dir here.


* Original commit: espressif/esp-idf@98a0cc783f
2022-05-27 17:44:23 +04:00
fc53888115 CI: Add a test to pre-check fuzzer tests compilation before weekly run
* Original commit: espressif/esp-idf@637f5c0a68
2022-05-27 17:44:23 +04:00
7635c0479b soc: descriptive part occupy whole component
* Original commit: espressif/esp-idf@79887fdc6c
2022-05-27 17:44:23 +04:00
d5fe42bffb Coredump config option rename throughout IDF
* Original commit: espressif/esp-idf@20af94ff53
2022-05-27 17:44:23 +04:00
e0bc60a586 mdns, dns, dhcp, dhcps: update fuzzer test to work in CI
Closes: IDF-1861 and IDF-1990


* Original commit: espressif/esp-idf@a43c06a592
2022-05-27 17:44:23 +04:00
9772e49b26 cmock: added cmock as component
* changing dependencies from unity->cmock
* added component.mk and Makefile.projbuild
* ignore test dir in gen_esp_err_to_name.py
* added some brief introduction of CMock in IDF


* Original commit: espressif/esp-idf@20c068ef3b
2022-05-27 17:44:23 +04:00
110f1f652d mdns test: Add test to resolve esp32 hostname with DiG
* Original commit: espressif/esp-idf@81e89476fe
2022-05-27 17:44:23 +04:00
ed77d65a82 examples: Common connect component: Unregister shutdown handler on disconnection
To be able to connect smoothly after disconnecting, we have to unregister all handlers including shutdown handler on disconnection


* Original commit: espressif/esp-idf@52a7721bf7
2022-05-27 17:44:23 +04:00
6021a88657 mdns: Support queries in responses in mDNS non-strict mode
By default adds original queries to responses in order to be resolved by some resolvers, such as lwIP mdns library. This functionality however is discouraged by the RFC6762, so it could be disabled in menuconfig if MDNS_STRICT_MODE configured

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


* Original commit: espressif/esp-idf@bcfa36db8f
2022-05-27 17:44:23 +04:00
78f71ecdf6 mdns: Fix include query ID in reponses
Closes https://github.com/espressif/esp-idf/issues/5574


* Original commit: espressif/esp-idf@f62e321d87
2022-05-27 17:44:23 +04:00
07f57523c8 examples: common connect to also support no connection flow if no inteface chosen
This is useful for testing if there's no need for external network and connection


* Original commit: espressif/esp-idf@085d2b8d25
2022-05-27 17:44:23 +04:00
ad67a23097 vfs: support vfs uart set line endings with specified uart number
* Original commit: espressif/esp-idf@8e00522cd7
2022-05-27 17:44:23 +04:00
078abd8161 esp_rom: extract common GPIO apis into esp_rom_gpio.h
* Original commit: espressif/esp-idf@a4d0033c03
2022-05-27 17:44:23 +04:00
f1f6c5de05 examples: common connect: fix build error if ipv6 disabled
Declaration of local variable esp_ip6_addr_t ip6[]; was active even if IPV6 disabled in sdkconfig. Introduced in 62e39adff8db0605875fe7103c8919fbfe229f20


* Original commit: espressif/esp-idf@5c6bca69a5
2022-05-27 17:44:23 +04:00
12fb6d8e15 protocol_examples_common: keep buffering enabled on stdout
- Newlib uses significantly more stack space when printing to an unbuffered stream
- For examples tests, disabling buffering on stdout is not really required

This issue was found during one of the OTA example test failure, root cause
being stack overflow in `esp_event` task.


* Original commit: espressif/esp-idf@7925ba245d
2022-05-27 17:44:23 +04:00
2258d5bcef mdns-example: fail gracefully if mdns response not received within timeout
If mdns answer hasn't been received within timeout, Value error would be raised, but the mdns-server-thread would still run, blocking CI jobs. Fixed by moving the raise statement within try-finally block


* Original commit: espressif/esp-idf@3758177bf8
2022-05-27 17:44:23 +04:00
54f5c6f29c examples: common connect component to use both interfaces at once
* Original commit: espressif/esp-idf@06711c7c36
2022-05-27 17:44:23 +04:00
3319844745 mdns: Allow config mDNS task stack size
Signed-off-by: Axel Lin <axel.lin@gmail.com>

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


* Original commit: espressif/esp-idf@cf7e48c779
2022-05-27 17:44:23 +04:00
ac70c9aba8 mdns: Remove mbedtls dependency
mdns does not use mbedtls, so remove mbedtls dependency.

Signed-off-by: Axel Lin <axel.lin@gmail.com>


* Original commit: espressif/esp-idf@f4a4549a34
2022-05-27 17:44:23 +04:00
123ae8db62 examples: add socket stdin utils to common connect component
* Original commit: espressif/esp-idf@a5a750ba48
2022-05-27 17:44:23 +04:00
e24cc7d1ae common_connect: add support for getting multiple IPv6 addresses
* Original commit: espressif/esp-idf@63aa0d6e9c
2022-05-27 17:44:23 +04:00
9d9aac1569 esp-netif: support for ipv6 addr types and indices
* Original commit: espressif/esp-idf@56725fa678
2022-05-27 17:44:23 +04:00
ef9a758662 Add multi-target support for performance tests
* Original commit: espressif/esp-idf@15884eccf2
2022-05-27 17:44:23 +04:00
2a23f355c7 examples: enable IPv6 in example common connect for esp32s2
Closes IDF-1115


* Original commit: espressif/esp-idf@0927ac648f
2022-05-27 17:44:23 +04:00
07399011f7 examples: common connect code to ignore GOT_IP6_EVENT if comes from unrelated netif
* Original commit: espressif/esp-idf@48fe3a13f5
2022-05-27 17:44:23 +04:00
2b7d43e1f8 mdns: limit the GOT_IP6_EVENT to only known network interfaces
* Original commit: espressif/esp-idf@ab8cab1c55
2022-05-27 17:44:23 +04:00
7bf23a7fe9 doc: Changed Chinese doc to use dynamic chip name
* Original commit: espressif/esp-idf@cfeb9e68cb
2022-05-27 17:44:23 +04:00
881ca095f0 doc: Update English pages with generic target name
* Original commit: espressif/esp-idf@9352899d69
2022-05-27 17:44:23 +04:00
d9fa457b4f docs: add new top-level docs builder that builds docs for a single chip
* Original commit: espressif/esp-idf@e6211c7864
2022-05-27 17:44:23 +04:00
4eb3e89841 esp32: add implementation of esp_timer based on TG0 LAC timer
Closes: IDF-979


* Original commit: espressif/esp-idf@739eb05bb9
2022-05-27 17:44:23 +04:00
74aee42f80 ethernet: work with cache disabled
add ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE flag, make ethenret driver
possible to work when cache disabled

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


* Original commit: espressif/esp-idf@5ad0bdd8db
2022-05-27 17:44:23 +04:00
b5e5a64e7f mdns: fixed typos in the variable names and the comments
* Original commit: espressif/esp-idf@ecca39e19f
2022-05-27 17:44:23 +04:00
6713ffedcc mdns: fix preset of esp_netif ptr for local interfaces
mdns module store local copy of esp_netif for common interfaces,
but it was correctly initialized only when interface started.
If the event were missed (e.g. mdns_init after interface start)
the local copy wouldn't be initialized. Fixed by restoring the local
copy.

Closes WIFI-1538


* Original commit: espressif/esp-idf@09e36f9f33
2022-05-27 17:44:23 +04:00
817c4fd2e8 mdns: fixed crash on event during deinit
mdns library deinitialization destroys internal structures including action queue. if an event (e.g. network update) received
after some essential stucture is destoyed, an unexpected behavour might be introduced (e.g. crash of adding the event notification
to the action queue which was already destroyed

Closes WIFI-1485


* Original commit: espressif/esp-idf@eaa2f12d67
2022-05-27 17:44:23 +04:00
8f0dc6d578 mdns: respond to discovery with the proper pseudo name _services._dns-sd._udp
Closes https://github.com/espressif/esp-idf/issues/4369
Closes IDFGH-2219


* Original commit: espressif/esp-idf@de17a1487f
2022-05-27 17:44:23 +04:00
7a3aa26df8 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-05-27 17:44:23 +04:00
c7ff8ba0c9 ethernet: warning when double start/stop
* Original commit: espressif/esp-idf@ac11545e0a
2022-05-27 17:44:23 +04:00
80ce63d73e ethernet: move netif glue && add ref counter
1. move netif glue into single file
2. add reference counter for Ethernet driver


* Original commit: espressif/esp-idf@c3ee156df0
2022-05-27 17:44:23 +04:00
d20666f3a0 mdns: fixed forgotten merge conflicts in debug code
* Original commit: espressif/esp-idf@d9433ef692
2022-05-27 17:44:23 +04:00
cd3cc0be3b ethernet: add gpio number into config structure
* Original commit: espressif/esp-idf@05d71319de
2022-05-27 17:44:23 +04:00
1734e59057 zh_CN translation of mdns service
* Original commit: espressif/esp-idf@9691a755f6
2022-05-27 17:44:23 +04:00
662a4ce050 mdns: add missing include of esp_task.h
* Original commit: espressif/esp-idf@5884b80908
2022-05-27 17:44:23 +04:00
fb1de80fd7 mdns: add configuration values for task priority, affinity and internal service timeouts
closes https://github.com/espressif/esp-idf/issues/4217


* Original commit: espressif/esp-idf@c6f38f04f8
2022-05-27 17:44:23 +04:00
1f35e9a728 tcpip_adapter: updated tcpip_adapter compatablity layer to include all
public API and keep 100% backward compatibility
update build of tcpip adapter when ethernet disabled


* Original commit: espressif/esp-idf@7f5cda1b82
2022-05-27 17:44:23 +04:00
573855031d esp_netif: extract wifi_netif module as an abstraction to wifi universal interface defined by if handle and callback
* Original commit: espressif/esp-idf@20add7da60
2022-05-27 17:44:23 +04:00
d1c62628b8 esp_netif and examples: using wifi driver handle, update examples and tests to pass the CI
* Original commit: espressif/esp-idf@3a19bf055d
2022-05-27 17:44:23 +04:00
48b819bbc1 mdns: update mdns to use esp-netif for mdns supported services such as STA, AP, ETH
removes also include dependency on lwip to use esp_netif defined address fields and structures


* Original commit: espressif/esp-idf@19e24fe61e
2022-05-27 17:44:23 +04:00
dfcefc38fd 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-05-27 17:44:23 +04:00
879a6cdfa3 examples: common component initialization code to use new esp_netif
instead of tcpip_adapter


* Original commit: espressif/esp-idf@21464465ea
2022-05-27 17:44:23 +04:00
53e2aa3241 esp_netif: Introduction of esp-netif component as a replacement of tcpip_adpter
- provides object oriented access to network intefaces
- not limited to default netifs
- more generic abstraction to network input output functions
- event handler registration removed from component responsibility
- backward compatibility layer for legacy tcpip_apapter APIs

Closes IDF-39


* Original commit: espressif/esp-idf@ffe043b1a8
2022-05-27 17:44:23 +04:00
3cc64469c1 examples: removed ip4addr_ntoa and used prefered IP2STR for displaying IP addresses
* Original commit: espressif/esp-idf@ec9f245dd3
2022-05-27 17:44:23 +04:00
248b11bb0a esp_event, mdns: fixes for CONFIG_ETH_ENABLED=n
* Original commit: espressif/esp-idf@569ad7545c
2022-05-27 17:44:23 +04:00
d52dddfea5 ci: limit example test to ESP32s
* Original commit: espressif/esp-idf@63329b169b
2022-05-27 17:44:23 +04:00
67d310b988 run WiFi on ESP32SBETA
* Original commit: espressif/esp-idf@99ef587a05
2022-05-27 17:44:23 +04:00
901124b7ee build and link hello-world for esp32s2beta
* Original commit: espressif/esp-idf@84b2f9f14d
2022-05-27 17:44:23 +04:00
f6ff165be9 mdns: fix crash for hostname queries
Receiving TXT, PTR, SDPTR and SRV record type queries would crash the application if the hostname
 was used as instance name.

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


* Original commit: espressif/esp-idf@3d1170031b
2022-05-27 17:44:23 +04:00
d8d6b35553 mdns: updated example test to exercise also hostnames resolved by lwip gethostbyname and getaddrinfo
* Original commit: espressif/esp-idf@f3cb91ef71
2022-05-27 17:44:23 +04:00
f44c569422 mdns: fix possible race condition when checking DHCP status on WIFI_EVENT_STA_CONNECTED event.
tcpip_adapter_dhcpc_get_status() returns the actual internal value of dhcp client without any locking or TCP/IP stack context call, so when CONNECTED event fired with default settings it started DHCP client in TCP/IP stack context and at the same time mdns event handler checking actual DHCP state, which could still be INIT (not STARTED). Purpose of this check is to enable PCB if DHCP was stopped before setting network interface up (typically static IP settings), so the solutin is to check against TCPIP_ADAPTER_DHCP_STOPPED state


* Original commit: espressif/esp-idf@7f410a0bcb
2022-05-27 17:44:23 +04:00
7dfe14c83d ethernet: support OpenCores ethernet MAC
OpenCores Ethernet MAC has a relatively simple interface, and is
already supported in QEMU. This makes it a good candidate for enabling
network support when running IDF apps in QEMU, compared to the
relatively more complex task of writing a QEMU model of ESP32 EMAC.

This driver is written with QEMU in mind: it does not implement or
handle things that aren't implemented or handled in the QEMU model:
error flags, error interrupts. The transmit part of the driver also
assumes that the TX operation is done immediately when the TX
descriptor is written (which is the case with QEMU), hence waiting for
the TX operation to complete is not necessary.

For simplicity, the driver assumes that the peripheral register
occupy the same memory range as the ESP32 EMAC registers, and the
same interrupt source number is used.


* Original commit: espressif/esp-idf@31dac92e5f
2022-05-27 17:44:23 +04:00
286c646725 mdns: use constant size of AAAA answer in mdns packets instead of deriving from lwip struct size, since the struct could contain also zones
closes WIFI-771


* Original commit: espressif/esp-idf@e5e31c5d01
2022-05-27 17:44:23 +04:00
0582187b9a examples: gracefully shut down Wi-Fi before restart
This fixes the issue that if Wi-Fi is stopped from a shutdown handler,
the code in connect.c tries to reconnect, and fails because Wi-Fi is
already stopped.
Also make the error check in connect.c less strict.


* Original commit: espressif/esp-idf@28a440521e
2022-05-27 17:44:23 +04:00
28d5b74a00 ethernet: update spi-ethernet api
* Original commit: espressif/esp-idf@546ac64a9e
2022-05-27 17:44:23 +04:00
c70d527d80 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-05-27 17:44:23 +04:00
7cdf96cffa mdns: fix missing bye packet if services removed with mdns_service_remove_all() or mdns_free()
Closes https://github.com/espressif/esp-idf/issues/3660


* Original commit: espressif/esp-idf@a001998d52
2022-05-27 17:44:23 +04:00
407875d9c5 mdns: mdns_service_remove_all doesn't take an argument
Merges https://github.com/espressif/esp-idf/pull/2937


* Original commit: espressif/esp-idf@c2764f6fe8
2022-05-27 17:44:23 +04:00
3e753f5e2d tools: Mass fixing of empty prototypes (for -Wstrict-prototypes)
* Original commit: espressif/esp-idf@afbaf74007
2022-05-27 17:44:23 +04:00
aaba3fc47a examples: change default build instructions in docs to CMake
* Original commit: espressif/esp-idf@e7dba7d7bc
2022-05-27 17:44:23 +04:00
144d4ad1d4 mdns: fix ignoring mdns packet with some invalid name entries in question field
In case of invalid name entry, only this entry is invalidated and parsing continues as other query entries could contain questions to be responded to


* Original commit: espressif/esp-idf@4bd4c7caf3
2022-05-27 17:44:23 +04:00
e431b6b7fe build system: Use CMake-based build system as default when describing commands
* Original commit: espressif/esp-idf@47bbb107a8
2022-05-27 17:44:23 +04:00
ed71a239a8 ethernet: support dm9051
1. move resource allocation from xxx_init to xxx_new
2. fix enabling tx checksum insertion by mistake
3. iperf example: enlarge max arguments
4. add examples for spi-ethernet

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


* Original commit: espressif/esp-idf@cb42c29252
2022-05-27 17:44:23 +04:00
680bad646f add esp_eth component
* Original commit: espressif/esp-idf@90c4827bd2
2022-05-27 17:44:23 +04:00
271665e0cb examples: use new component registration api
* Original commit: espressif/esp-idf@6771eead80
2022-05-27 17:44:23 +04:00
7fb6686716 components: use new component registration api
* Original commit: espressif/esp-idf@9eccd7c082
2022-05-27 17:44:23 +04:00
4912bef740 mdns: fix static analysis warnings
* Original commit: espressif/esp-idf@c34de4cba6
2022-05-27 17:44:23 +04:00
181a22ec2b mdns: added initial suite of api unit tests
* Original commit: espressif/esp-idf@e6801912c5
2022-05-27 17:44:23 +04:00
4172219225 mdns tests: adapt mdns fuzzer test to compile with event loop library
* Original commit: espressif/esp-idf@38d15cbd63
2022-05-27 17:44:23 +04:00
9a0803ad7e Rename Kconfig options (examples)
* Original commit: espressif/esp-idf@151f757912
2022-05-27 17:44:23 +04:00
05ddd5f0e4 Rename Kconfig options (components/esp32)
* Original commit: espressif/esp-idf@0ae53691ba
2022-05-27 17:44:23 +04:00
7e3b35efd9 mdns: documentation: fixed inconsistent enum in example of reading mdns results
* Original commit: espressif/esp-idf@786f5641f7
2022-05-27 17:44:23 +04:00
98d2c1a073 mdns: fixed mdns crashing on reception of txt packet without a corresponding service
closes #2866


* Original commit: espressif/esp-idf@af48977f21
2022-05-27 17:44:23 +04:00
84cbb1f3cf mdns: use const char* for mdns txt items types to remove warning when assigning
* Original commit: espressif/esp-idf@c050a75616
2022-05-27 17:44:23 +04:00
4c6818ee97 mdns: updated doxygen comments documenting mdns api
Closes https://github.com/espressif/esp-idf/issues/1718


* Original commit: espressif/esp-idf@a851aac255
2022-05-27 17:44:23 +04:00
c440114d14 mdns: update mdns_out_question_s to be in line with mdns_parsed_question_s struct
Closes  https://github.com/espressif/esp-idf/issues/1568


* Original commit: espressif/esp-idf@eddd5c4f2c
2022-05-27 17:44:23 +04:00
c0f65a6997 mdns example: Remove a warned unused constant
* Original commit: espressif/esp-idf@453a33ce79
2022-05-27 17:44:23 +04:00
778eaa6ced examples/protocols/mdns: use common network component
* Original commit: espressif/esp-idf@9628c60a1d
2022-05-27 17:44:23 +04:00
25f8656fbc examples: add component for protocol examples network functionality
* Original commit: espressif/esp-idf@22bef90bd3
2022-05-27 17:44:23 +04:00
6ea0ea93fa mdns: use esp_event library to handle events
* Original commit: espressif/esp-idf@a2d59525e5
2022-05-27 17:44:23 +04:00
d9aec9fc71 fuzzer tests: update of mdns and lwip host compilation for fuzzer testing
Closes IDF-540


* Original commit: espressif/esp-idf@bc60bbbeaf
2022-05-27 17:44:23 +04:00
985e69117e mdns: fix possible crash when probing on particular interface with duplicated service instances due to naming conflicts on network
Issue: MDNS server initially sends probing packets to resolve naming confilicts with already registered service instances. In case of a conflict, instance name is altered and probing restarts. Original instance however wasnnot removed from the structure and upon service removal only one entry was removed and a dangling service might have been kept in the structure to bring about a crash.
Resolution: Keep only one instance of a service in the probing structure.

Closes IDF-498


* Original commit: espressif/esp-idf@265e983a45
2022-05-27 17:44:23 +04:00
75deebbf03 mdns: enable pcbs before starting service thread to avoid updating pcb's internal variables from concurent tasks
possible race condition: user task runs mdns_init, which enables pcbs while mdns-task already created could execute enable/disable of the same pcbs if an appropriate system event received


* Original commit: espressif/esp-idf@c87f0cb6ca
2022-05-27 17:44:23 +04:00
fdd27dc9fa mdns: fix possible deadlock on mdns deinit calling mdns_free()
mnds_free() initiates stop and delete timer tasks, which after locking the mutex could lead to a dead lock in case timer task executed before deleting the task, as it would wait indefinitelly for unlocking the mutex. This condition is fixed by calling _mdns_stop_timer without locking the mutex, because there's no need to protect any data when stopping and deleting the timer task

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


* Original commit: espressif/esp-idf@48b5501c25
2022-05-27 17:44:23 +04:00
2ec3b558ea mdsn: fix race condition in updating packet data from user task when failed to allocate or queue a new service
Issue: mdns_service_add API allocates and queues an action to be processed in mdns task context; when allocation or queueing fails, allocated structure needs to be freed. Function _mdns_free_service did not only fee all the structures, but also updates packet data.
Resolution: Moved removal of packet data outside of _mdns_free_service function.


* Original commit: espressif/esp-idf@021dc5d453
2022-05-27 17:44:23 +04:00
450cbf03cf mdns: fix possible crash when packet scheduled to transmit contained service which might have been already removed
packets scheduled to transmit are pushed to action queue and removed from tx_queue_head structure, which is searched for all remaining services and while service is removed, then service questions/asnwers are also removed from this structure. This update fixes possible crash when packet is pushed to action queue, and when service is removed, its answers are removed from tx_queue_head, but not from action queue. this could lead to a crash when the packet is poped from action queue containing questions/answers to already removed (freed) service

Closes IDF-504


* Original commit: espressif/esp-idf@67051a286b
2022-05-27 17:44:23 +04:00
34f6d8dd33 mdns: use binary semaphore instead of mutex when searching
mdns_search_once_t::lock is used to synchronize tasks (taken by one
task and given by the other) so it should not be a mutex.
Convert to semaphore, and rename to indicate its purpose.


* Original commit: espressif/esp-idf@eef0b5090a
2022-05-27 17:44:22 +04:00
b6efc688b5 mdns: fix memory leak in pbuf if tcpipadapter failed to get netif
* Original commit: espressif/esp-idf@8462751f95
2022-05-27 17:44:22 +04:00
11e4aebefd mdns example: fix print result for IPv6 addresses
* Original commit: espressif/esp-idf@fbef5297fc
2022-05-27 17:44:22 +04:00
90e4babc61 mdns: fix malfuctional query_txt
when running a query for a single txt, result entries were not created and attached to result structure. this issue was introduced when fixing memory leak in txt structure, which worked correctly for PTR queries, but caused trouble for TXT query.


* Original commit: espressif/esp-idf@1a027734af
2022-05-27 17:44:22 +04:00
c546ab8dea mdns: fix possible crash when mdns_free called while action queue not empty
* Original commit: espressif/esp-idf@206b47c03a
2022-05-27 17:44:22 +04:00
6582b41cd1 mdns: fix memory leak when query for service receives multiple ptr entries for one instance
fixes redmine issue 27300


* Original commit: espressif/esp-idf@9a4da97fb4
2022-05-27 17:44:22 +04:00
358d26c8a1 mdns: fix crash after init if no memory for task
mdns init first starts timer task, then starts service task. if service task failed to be created, timer task needs to be stopped too.
fixed https://ezredmine.espressif.cn:8765/issues/28466


* Original commit: espressif/esp-idf@a47768dc4e
2022-05-27 17:44:22 +04:00
8d08e5ed95 mdns tests: execute test services only when running example in ci
Test services may cause confussion (and did cause some GitHub/forum issues). This update runs test services only when example executed in ci. Also host name is a simple config entry if executed outside of ci.


* Original commit: espressif/esp-idf@74cc7a065f
2022-05-27 17:44:22 +04:00
2ac83d0f27 mdns: fixed crash on free undefined ptr after skipped strdup
Shortcircuit evaluation may cause skip of _mdns_strdup_check of any further question field, which after clear_rx_packet freed undefined memory.
Fixes https://ezredmine.espressif.cn:8765/issues/28465


* Original commit: espressif/esp-idf@e0a8044a16
2022-05-27 17:44:22 +04:00
98e3171db6 Correct Kconfigs according to the coding style
* Original commit: espressif/esp-idf@37126d3451
2022-05-27 17:44:22 +04:00
2f85c075be mdns: fix networking running udp_sendif from lwip thread
* Original commit: espressif/esp-idf@f7d4a4be6a
2022-05-27 17:44:22 +04:00
b30a7fec27 mdns: fixed static memory leak
* Original commit: espressif/esp-idf@6bb68a5a75
2022-05-27 17:44:22 +04:00
7a4fdad16d mdns: check all mallocs for failure and add default hook to log error with free heap
solves crash about _mdns_result_txt_create when stress test


* Original commit: espressif/esp-idf@c8cb4cd3c8
2022-05-27 17:44:22 +04:00
b4e57424f9 mdns: resolve memory leak when txt record received multiple times
* Original commit: espressif/esp-idf@a6b2b73f03
2022-05-27 17:44:22 +04:00
2763bcdb8d mdns: skip sending search when finished, not properly locked timer task
* Original commit: espressif/esp-idf@31163f02d5
2022-05-27 17:44:22 +04:00
dce0b26ef8 examples: Fix Python coding style
* Original commit: espressif/esp-idf@57c54f96f1
2022-05-27 17:44:22 +04:00
ade4aeffa5 mdns: added example test for ci runners
* Original commit: espressif/esp-idf@6309643c1d
2022-05-27 17:44:22 +04:00
8cd0e8a501 mdns: sending search packets also in probing and announcing state
mdns queries did not work properly when send imeadiately after set_hostname, which cuased reinit of pcb and therefore restarted probing, so search packets were blocked until probing finished
closes #2507, closes #2593


* Original commit: espressif/esp-idf@d16762a036
2022-05-27 17:44:22 +04:00
9b3b41c3f1 mdns: fixed crashes on network changes
1) two events AP_STOP, AP_START shortly after each other may cause IGMP config on already stopped netif
2) not properly locked sending packets to queue from timer task

closes #2580


* Original commit: espressif/esp-idf@097282a8e3
2022-05-27 17:44:22 +04:00
ea2300753e Update network code for mDNS to work with newest LwIP
- Uses one PCB that listens to all interfaces
- Manages multicast groups for each interface


* Original commit: espressif/esp-idf@3ec0e7e2d2
2022-05-27 17:44:22 +04:00
81c219d4ee feat(mdns): fix bug when clean action memory
* Original commit: espressif/esp-idf@3d4deb9726
2022-05-27 17:44:22 +04:00
0c17121ad7 bugfix: mdns_service_txt_set() wasn't allocating memory for TXT records
Allocation was happening later, causing possible use of stack variables
of caller function, which could be invalid.

Signed-off-by: Piyush Shah <piyush@espressif.com>


* Original commit: espressif/esp-idf@e5e2702ca3
2022-05-27 17:44:22 +04:00
67173f6770 cmake: make main a component again
* Original commit: espressif/esp-idf@d9939cedd9
2022-05-27 17:44:22 +04:00
fed787f54f Feature/sync lwip as submodule
* Original commit: espressif/esp-idf@3578fe39e0
2022-05-27 17:44:22 +04:00
b4ab30b5de mdns: Fix a portion of the queries are issued with the wildcard query type
* Original commit: espressif/esp-idf@f3f0445f4d
2022-05-27 17:44:22 +04:00
dd714947d6 added CI job for AFL fuzzer tests
* Original commit: espressif/esp-idf@0c147648f7
2022-05-27 17:44:22 +04:00
39de491597 mdns: Minor fix for mdns_service_remove()
Send the Goodbye packet while removing an mDNS service as an "Authoritative" packet so
that the listeners remove the service from their records immediately.


* Original commit: espressif/esp-idf@5c7eb7e27b
2022-05-27 17:44:22 +04:00
19acac76eb Replace all DOS line endings with Unix
Command run was:
git ls-tree -r HEAD --name-only | xargs dos2unix


* Original commit: espressif/esp-idf@a67d5d89e0
2022-05-27 17:44:22 +04:00
0191d6fcd7 fix(mdns): add the maximum number of services
* Original commit: espressif/esp-idf@ba458c69cf
2022-05-27 17:44:22 +04:00
b26c8665f8 fix(mdns): fix the exception when remove one of multiple service
* Original commit: espressif/esp-idf@696d733eb0
2022-05-27 17:44:22 +04:00
98069f9ca2 remove executable permission from source files
* Original commit: espressif/esp-idf@cb649e452f
2022-05-27 17:44:22 +04:00
ad29d34bb6 Fixed nullptr dereference in MDNS.c
* Original commit: espressif/esp-idf@fffbf7b750
2022-05-27 17:44:22 +04:00
9f1be3668e MDNS-Fuzzer: AFL fuzzer tests for mdsn packet parser
* Original commit: espressif/esp-idf@e983230be9
2022-05-27 17:44:22 +04:00
450c9de67b Fix potential NULL pointer dereference crash.
* Original commit: espressif/esp-idf@be707f1c6b
2022-05-27 17:44:22 +04:00
c7701d41f8 cmake: Add component dependency support
Components should set the COMPONENT_REQUIRES & COMPONENT_PRIVATE_REQUIRES variables to define their
requirements.


* Original commit: espressif/esp-idf@1cb5712463
2022-05-27 17:44:22 +04:00
b9726db48e cmake: Add CMakeLists.txt files for all examples
Generating using convert_to_cmake.py, with some minor cleanup


* Original commit: espressif/esp-idf@800bffb8b0
2022-05-27 17:44:22 +04:00
f1ccc4052e cmake: Remove defaults for COMPONENT_SRCDIRS, COMPONENT_SRCS, COMPONENT_ADD_INCLUDEDIRS
* Philosophical: "explicit is better than implicit".
* Practical: Allows useful errors if invalid directories given in components as the defaults aren't
  always used. Also trims the -I path from a number of components that have no actual include
  directory.
* Simplifies knowing which components will be header-only and which won't


* Original commit: espressif/esp-idf@4f1a856dbf
2022-05-27 17:44:22 +04:00
84bd1d7e88 build system: Initial cmake support, work in progress
* Original commit: espressif/esp-idf@c671a0c3eb
2022-05-27 17:44:22 +04:00
259d3fc609 fix the bug that in mdns test code redefine esp_err_t to uint32_t, which should be int32_t
* Original commit: espressif/esp-idf@81e4cad615
2022-05-27 17:44:22 +04:00
6d99957f2d Fix Kconfig.projbuild in some examples, where myssid is erroneously kept as the default value for password.
Signed-off-by: Hrishikesh Dhayagude <hrishi@espressif.com>


* Original commit: espressif/esp-idf@59d19d12af
2022-05-27 17:44:22 +04:00
7784d002fc Fix exception when service is removed while there are pending packets that depend on it
* Original commit: espressif/esp-idf@421c6f154b
2022-05-27 17:44:22 +04:00
9ebd9852ca Examples: Demonstrate the usage of esp_err_to_name
* Original commit: espressif/esp-idf@27a63c492f
2022-05-27 17:44:22 +04:00
26f00e8ddb Moved files into separate folders per 'en' and 'zh_CN' language version and linked 'zh_CN' files back to 'en' files if translation is not yet available
* Original commit: espressif/esp-idf@097adc3a33
2022-05-27 17:44:22 +04:00
bce7d5231c mdns: Fix case where service is NULL and that will cause exception
* Original commit: espressif/esp-idf@4fa130ae4f
2022-05-27 17:44:22 +04:00
ef924f1aa5 mdns: Fix issue with some mDNS parsers
Some mDNS parser have issue with zero terminated TXT lists. This fix targets to overcome this issue. Found and tested with jmdns.


* Original commit: espressif/esp-idf@51dde19a76
2022-05-27 17:44:22 +04:00
ad8c92db52 Import mDNS changes
* Original commit: espressif/esp-idf@4bddbc031c
2022-05-27 17:44:22 +04:00
3aa605fe24 Fix compilation errors when using gcc-7.2.0 for the crosstool-ng toolchain
* Change snprintf for strlcat does not complain w/gcc7.2.0 and it is safer, thanks @projectgus
* Use proper quotes for character literals

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


* Original commit: espressif/esp-idf@519edc332d
2022-05-27 17:44:22 +04:00
00a72b8920 components/mdns: wrong Message compression detect
Old behavior assumes message compressed when any of 2 most significant bits are set,
But in fact Message compressed only when both those bits are set to 1.

Also maximal label length should be 63 bytes.


* Original commit: espressif/esp-idf@6e24566186
2022-05-27 17:44:22 +04:00
907e7ee29e mdns: fix leak after _mdns_create_service if we have a malloc error.
* Original commit: espressif/esp-idf@b6b36bd9dd
2022-05-27 17:44:22 +04:00
b367484361 Use LwIP IPC for low-level API calls
* Original commit: espressif/esp-idf@713964fe9e
2022-05-27 17:44:22 +04:00
4a8582f500 Add AFL fuzz test
* Original commit: espressif/esp-idf@4c2622755d
2022-05-27 17:44:22 +04:00
75de31cea3 implement fixes for issues found while fuzz testing
* Original commit: espressif/esp-idf@99d39909c4
2022-05-27 17:44:22 +04:00
4acf639afc mdns: add simple dns-sd meta query support
tabs to spaces

match domain


* Original commit: espressif/esp-idf@96e8a3c725
2022-05-27 17:44:22 +04:00
1a1cf71a84 examples: Standardise naming of files, symbols, etc. in examples
* Use "example" in all example function & variable names,
  ie use i2c_example_xxx instead of i2c_xxx for example functions.
  Closes #198 https://github.com/espressif/esp-idf/issues/198
* Mark example functions, etc. static
* Replace uses of "test" & "demo" with "example"
* Split the UART example into two
* Rename "main" example files to end with "_main.c" for disambiguation


* Original commit: espressif/esp-idf@821c70f5d7
2022-05-27 17:44:22 +04:00
5924dafd94 examples: check return value of nvs_flash_init
nvs_flash_init may return an error code in some cases, and applications
should check this error code (or at least assert on it being ESP_OK, to
make potential issues more immediately obvious).

This change modifies all the examples which use NVS to check the error
code. Most examples get a simple ESP_ERROR_CHECK assert, while NVS
examples, OTA example, and NVS unit tests get a more verbose check which
may be used in real applications.


* Original commit: espressif/esp-idf@4813ab2d28
2022-05-27 17:44:22 +04:00
91bb5095f5 address security issues with mDNS
* Original commit: espressif/esp-idf@c89e11c8fa
2022-05-27 17:44:22 +04:00
caa4884b3a Added README.md to example category folders
* Original commit: espressif/esp-idf@0b6598c492
2022-05-27 17:44:22 +04:00
441a53c604 Moved examples to new folders / categories. Removed example numbers from example names
* Original commit: espressif/esp-idf@382999b378
2022-05-27 17:44:22 +04:00
7fbf8e5247 Initial mDNS component and example
* Original commit: espressif/esp-idf@dd3f18d2d8
2022-05-27 17:44:22 +04:00
7346ed9765 Merge pull request #24 from gabsuren/ci/websocket_add_test_job
CI: Added pytest example for websocket (IDFGH-7253)
2022-05-26 11:37:39 +02:00
76298ff70e CI: Added CI example run job 2022-05-25 20:12:38 +04:00
0875008a46 Merge pull request #30 from Sjurinho/feature/extended-simcom-support
Feature/extended simcom support (IDFGH-7243)
2022-05-25 13:21:50 +02:00
a661e51f7e esp_modem: Fix param description for get_operator_name()
Co-authored-by: Franz Höpfinger <krone-trailer@franzhoepfinger.de>
2022-05-24 17:44:35 +02:00
3ced2d9709 Update: Removed stringstream to decrease static sizes 2022-05-24 14:54:24 +02:00
ba88d7fdbc Update: Renamed list_in to be type-specific
Update: Removed modem-specific comment
2022-05-24 14:53:58 +02:00
4f3c4299d6 Update: Changed tests to coincide with new initial esp_modem mode 2022-05-24 14:53:11 +02:00
2a4e684848 Merge pull request #34 from david-cermak/bugfix/modem_cmux_uih
fix(esp_modem): Correction of switching to CMUX mode
2022-05-24 10:38:59 +02:00
541b391c3e Merge pull request #40 from gabsuren/docs/esp_websocket_client_readme
DOC: Updated README.md regarding esp_websocket_client
2022-05-24 10:28:10 +02:00
279a3cb6e0 DOC: Updated README.md regarding esp_websocket_client 2022-05-23 17:08:35 +04:00
60bb0eea99 Merge pull request #36 from diplfranzhoepfinger/feature/dte
typo (IDFGH-7360)
2022-05-15 15:54:49 +02:00
52de8f1b2a typo 2022-05-13 17:29:34 +02:00
503218b3ba fix(esp_modem): Correction of switching to CMUX
* BG96 needs a small pause between CMUX command reception and actual use
of the CMUX mode
* Correct CMUX payload reception to check FT_UIH message type (P/F flag
could be cleared or set)

Closes https://github.com/espressif/esp-protocols/issues/33
2022-05-10 18:04:18 +02:00
38149c8d9b Merge pull request #22 from 0xFEEDC0DE64/websocket_client_change_ping_interval
Add methods to allow get/set of websocket client ping interval (IDFGH-7096)
2022-05-03 09:26:54 +02:00
1984da150d Merge pull request #23 from 0xFEEDC0DE64/websocket_client_error
Implement websocket client connect error (IDFGH-7245)
2022-04-27 15:52:17 +02:00
9e37f537bd Implement websocket client connect error 2022-04-26 11:15:57 +02:00
e55f54b69e Add methods to allow get/set of websocket client ping interval 2022-04-26 11:06:20 +02:00
6e4d8a19ed Merge pull request #29 from MacDue/expose_fin
esp_websocket_client: Expose frame fin flag in websocket event
2022-04-26 08:47:15 +02:00
e98cf16859 Update: Removed duplicate power down function
Add: gnss power mode function
2022-04-20 09:37:39 +02:00
be3d2ece55 Update: Formatting 2022-04-20 09:16:05 +02:00
15ed885035 Add: Sim7600 extended support 2022-04-19 16:23:22 +02:00
b72a9ae710 esp_websocket_client: Expose frame fin flag in websocket event 2022-04-15 17:06:03 +01:00
0733ea8ff4 Add: Support for SIM7000 modules 2022-04-13 15:36:15 +02:00
15cbc9bd50 Update: modem_mode::UNDEF initially 2022-04-13 13:44:11 +02:00
bb7f198bea Add: sim7070 support 2022-04-13 13:42:57 +02:00
d3f7ea67fb Add: Expanded modem_command_library with more "standard" commands 2022-04-13 13:41:56 +02:00
6e5bede4c7 Merge pull request #28 from bitron/esp-modem_sim800_fix
esp_modem fix for SIM800 modem devices (IDFGH-7166)
2022-04-12 16:07:05 +02:00
d191a720d1 esp_modem: Fix DCE selection in ‘pppos_client’ example 2022-04-07 17:08:54 +02:00
cc0d52793b esp_modem: Fix AT command in ‘set_data_mode_sim8xx()’ for SIM800 modem device 2022-04-07 17:02:12 +02:00
804a8d5df6 Merge pull request #25 from tore-espressif/feature/usb_dte
Add USB DTE example
2022-04-04 16:05:29 +02:00
74040cfd1a esp_modem: Extend modem_console example with USB DTE 2022-04-04 15:48:47 +02:00
16ea3f7eb8 Merge pull request #21 from tore-espressif/fix/idf5.0
Fix IDFv5.0 include paths (IDFGH-6972)
2022-03-22 14:38:09 +01:00
1f5eb396c5 Fix IDFv5.0 include paths
Changed in commit a9fda54d39d1321005c3bc9b3cc268d0b7e9f052
2022-03-16 08:30:03 +01:00
827bebd2bc Merge pull request #20 from antmak/bugfix/passing_cmake_cxx_std_option
build: fix issue with passing cxx_std option, a common workaround (IDFGH-6876)
2022-03-04 12:49:51 +01:00
745201b188 build: fix issue with passing cxx_std option, a common workaround
issue: https://gitlab.kitware.com/cmake/cmake/-/issues/23297
2022-03-04 18:24:44 +07:00
ca266cabd0 Merge pull request #16 from david-cermak/update/modem_test_dynamic_poly
add(esp_modem): Add unit test to check polymorphic delete
2022-03-04 08:45:20 +01:00
e54b240870 add(esp_modem): Add unit test to check polymorphic delete 2022-03-04 08:37:22 +01:00
4ccc32ffb5 Merge pull request #19 from david-cermak/bugfix/fix_docs_build
CI: Fix docs build failure
2022-03-04 08:18:53 +01:00
91a177edd4 fix(ws_client): Docs to refer esp-protocols 2022-03-04 07:57:24 +01:00
f303cdc70b fix(CI): build docs & publish component failure 2022-03-04 07:44:45 +01:00
ab8c2da395 Merge pull request #18 from gabsuren/esp_websocket_client_migration_with_history
websocket: Initial version based on IDF 5.0 with history
2022-03-02 13:43:34 +01:00
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
04a7643d67 Merge pull request #17 from mensi/implement-sync
Implement esp_modem_sync for the C API
2022-02-28 10:05:21 +01:00
8b3d420055 Implement esp_modem_sync for the C API 2022-02-23 17:49:58 +01:00
50b083a58c Merge pull request #14 from b1ackviking/fix-missing-virtual-dtors
fix: add virtual destructors to ModuleIf and CommandableIf
2022-02-23 11:01:48 +01: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
5d27b2681a Merge pull request #10 from david-cermak/bugfix/modem_parse_battery_status
fix(esp_modem): Fix battery status parser
2021-12-07 16:03:00 +01: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
187 changed files with 19131 additions and 119 deletions

View File

@ -0,0 +1,76 @@
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@v1
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
working-directory: components/esp_websocket_client/examples/
run: |
. ${IDF_PATH}/export.sh
cat sdkconfig.ci >> sdkconfig.defaults
idf.py build
- name: Merge binaries
working-directory: components/esp_websocket_client/examples/build
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
- uses: actions/upload-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/esp_websocket_client/examples/build/
if-no-files-found: error
run-target:
name: Run Example Test on target
needs: build
strategy:
fail-fast: false
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
container:
image: python:3.7-buster
options: --privileged # Privileged mode has access to serial ports
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/esp_websocket_client/examples/build/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install -r $GITHUB_WORKSPACE/components/esp_websocket_client/examples/requirements.txt
- name: Download Example Test to target
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
- name: Run Example Test on target
working-directory: components/esp_websocket_client/examples
run: |
cp sdkconfig.ci sdkconfig.defaults
pytest --log-cli-level DEBUG --junit-xml=./test_app_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
- uses: actions/upload-artifact@v2
if: always()
with:
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: examples/*.xml

View File

@ -0,0 +1,35 @@
name: Build mDNS
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
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/mdns/examples/
cat sdkconfig.ci.eth_def >> sdkconfig.defaults
idf.py build
rm sdkconfig.defaults
cat sdkconfig.ci.eth_custom_netif >> sdkconfig.defaults
idf.py build
rm sdkconfig.defaults
cat sdkconfig.ci.eth_socket >> sdkconfig.defaults
idf.py build
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_apps/
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

@ -21,20 +21,36 @@ jobs:
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark
python -m pip install breathe recommonmark esp-docs
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/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/components/mdns/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_modem/index.html">esp-modem</a>' > index.html
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
- name: Upload components to component service
uses: espressif/github-actions/upload_components@master
with:
directories: "components/esp_modem"
name: "esp_modem"
directories: "components/esp_modem;components/esp_websocket_client;components/mdns"
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

30
.github/workflows/test_afl_fuzzer.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: AFL fuzzer compilation test
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: Install Necessary Libs
run: |
apt-get update -y
apt-get install -y libbsd-dev
- 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/mdns/tests/test_afl_fuzz_host/
make INSTR=off

View File

@ -8,3 +8,14 @@
* Brief introduction [README](components/esp_modem/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
### mDNS
* Brief introduction [README](components/mdns/README.md)
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
### esp_websocket_client
* Brief introduction [README](components/esp_websocket_client/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "connect.c" "stdin_out.c" "addr_from_stdin.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES esp_netif driver
)

View File

@ -0,0 +1,322 @@
menu "Example Connection Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config EXAMPLE_CONNECT_WIFI
bool "connect using WiFi interface"
default y
help
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
Choose this option to connect with WiFi
if EXAMPLE_CONNECT_WIFI
config EXAMPLE_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config EXAMPLE_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_WIFI_SCAN_METHOD
prompt "WiFi Scan Method"
default EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
help
WiFi scan method:
If "Fast" is selected, scan will end after find SSID match AP.
If "All Channel" is selected, scan will end after scan all the channel.
config EXAMPLE_WIFI_SCAN_METHOD_FAST
bool "Fast"
config EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
bool "All Channel"
endchoice
menu "WiFi Scan threshold"
config EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD
int "WiFi minimum rssi"
range -127 0
default -127
help
The minimum rssi to accept in the scan mode.
choice EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD
prompt "WiFi Scan auth mode threshold"
default EXAMPLE_WIFI_AUTH_OPEN
help
The weakest authmode to accept in the scan mode.
config EXAMPLE_WIFI_AUTH_OPEN
bool "OPEN"
config EXAMPLE_WIFI_AUTH_WEP
bool "WEP"
config EXAMPLE_WIFI_AUTH_WPA_PSK
bool "WPA PSK"
config EXAMPLE_WIFI_AUTH_WPA2_PSK
bool "WPA2 PSK"
config EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK
bool "WPA WPA2 PSK"
config EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE
bool "WPA2 ENTERPRISE"
config EXAMPLE_WIFI_AUTH_WPA3_PSK
bool "WPA3 PSK"
config EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK
bool "WPA2 WPA3 PSK"
config EXAMPLE_WIFI_AUTH_WAPI_PSK
bool "WAPI PSK"
endchoice
endmenu
choice EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD
prompt "WiFi Connect AP Sort Method"
default EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
help
WiFi connect AP sort method:
If "Signal" is selected, Sort matched APs in scan list by RSSI.
If "Security" is selected, Sort matched APs in scan list by security mode.
config EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
bool "Signal"
config EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
bool "Security"
endchoice
endif
config EXAMPLE_CONNECT_ETHERNET
bool "connect using Ethernet interface"
default n
help
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
Choose this option to connect with Ethernet
if EXAMPLE_CONNECT_ETHERNET
config EXAMPLE_USE_SPI_ETHERNET
bool
choice EXAMPLE_ETHERNET_TYPE
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_W5500
help
Select which kind of Ethernet will be used in the example.
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Select internal Ethernet MAC controller.
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select EXAMPLE_USE_SPI_ETHERNET
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
config EXAMPLE_USE_W5500
bool "W5500 Module"
select EXAMPLE_USE_SPI_ETHERNET
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_W5500
help
Select external SPI-Ethernet module (W5500).
config EXAMPLE_USE_OPENETH
bool "OpenCores Ethernet MAC (EXPERIMENTAL)"
select ETH_USE_OPENETH
help
When this option is enabled, the example is built with support for
OpenCores Ethernet MAC, which allows testing the example in QEMU.
Note that this option is used for internal testing purposes, and
not officially supported. Examples built with this option enabled
will not run on a real ESP32 chip.
endchoice # EXAMPLE_ETHERNET_TYPE
if EXAMPLE_USE_INTERNAL_ETHERNET
choice EXAMPLE_ETH_PHY_MODEL
prompt "Ethernet PHY Device"
default EXAMPLE_ETH_PHY_IP101
help
Select the Ethernet PHY device to use in the example.
config EXAMPLE_ETH_PHY_IP101
bool "IP101"
help
IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
config EXAMPLE_ETH_PHY_RTL8201
bool "RTL8201/SR8201"
help
RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
config EXAMPLE_ETH_PHY_LAN87XX
bool "LAN87xx"
help
Below chips are supported:
LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
flexPWR® Technology.
LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver
with HP Auto-MDIX and flexPWR® Technology.
LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
flexPWR® Technology.
Goto https://www.microchip.com for more information about them.
config EXAMPLE_ETH_PHY_DP83848
bool "DP83848"
help
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
config EXAMPLE_ETH_PHY_KSZ80XX
bool "KSZ80xx"
help
With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX
Ethernet Physical Layer Tranceivers (PHY).
The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041,
KSZ8051, KSZ8061, KSZ8081, KSZ8091
Goto https://www.microchip.com for more information about them.
endchoice
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with the SPI Ethernet Controller.
config EXAMPLE_ETH_SPI_SCLK_GPIO
int "SPI SCLK GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 14
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_SPI_MOSI_GPIO
int "SPI MOSI GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 13
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_SPI_MISO_GPIO
int "SPI MISO GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 12
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_SPI_CS_GPIO
int "SPI CS GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 15
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 80
default 36
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_ETH_SPI_INT_GPIO
int "Interrupt GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 4
help
Set the GPIO number used by the SPI Ethernet module interrupt line.
endif # EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
range -1 ENV_GPIO_OUT_RANGE_MAX
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endif # EXAMPLE_CONNECT_ETHERNET
config EXAMPLE_CONNECT_IPV6
bool "Obtain IPv6 address"
default y
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET
select LWIP_IPV6
help
By default, examples will wait until IPv4 and IPv6 local link addresses are obtained.
Disable this option if the network does not support IPv6.
Choose the preferred IPv6 address type if the connection code should wait until other than
the local link address gets assigned.
Consider enabling IPv6 stateless address autoconfiguration (SLAAC) in the LWIP component.
if EXAMPLE_CONNECT_IPV6
choice EXAMPLE_CONNECT_PREFERRED_IPV6
prompt "Preferred IPv6 Type"
default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
help
Select which kind of IPv6 address the connect logic waits for.
config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
bool "Local Link Address"
help
Blocks until Local link address assigned.
config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL
bool "Global Address"
help
Blocks until Global address assigned.
config EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL
bool "Site Local Address"
help
Blocks until Site link address assigned.
config EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL
bool "Unique Local Link Address"
help
Blocks until Unique local address assigned.
endchoice
endif
endmenu

View File

@ -0,0 +1,68 @@
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "lwip/sockets.h"
#include <lwip/netdb.h>
#include <arpa/inet.h>
#define HOST_IP_SIZE 128
esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_storage *dest_addr)
{
char host_ip[HOST_IP_SIZE];
int len;
static bool already_init = false;
// this function could be called multiple times -> make sure UART init runs only once
if (!already_init) {
example_configure_stdin_stdout();
already_init = true;
}
// ignore empty or LF only string (could receive from DUT class)
do {
fgets(host_ip, HOST_IP_SIZE, stdin);
len = strlen(host_ip);
} while (len<=1 && host_ip[0] == '\n');
host_ip[len - 1] = '\0';
struct addrinfo hints, *addr_list, *cur;
memset( &hints, 0, sizeof( hints ) );
// run getaddrinfo() to decide on the IP protocol
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = sock_type;
hints.ai_protocol = IPPROTO_TCP;
if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) {
return ESP_FAIL;
}
for( cur = addr_list; cur != NULL; cur = cur->ai_next ) {
memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr));
if (cur->ai_family == AF_INET) {
*ip_protocol = IPPROTO_IP;
*addr_family = AF_INET;
// add port number and return on first IPv4 match
((struct sockaddr_in*)dest_addr)->sin_port = htons(port);
freeaddrinfo( addr_list );
return ESP_OK;
}
#if CONFIG_LWIP_IPV6
else if (cur->ai_family == AF_INET6) {
*ip_protocol = IPPROTO_IPV6;
*addr_family = AF_INET6;
// add port and interface number and return on first IPv6 match
((struct sockaddr_in6*)dest_addr)->sin6_port = htons(port);
((struct sockaddr_in6*)dest_addr)->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
freeaddrinfo( addr_list );
return ESP_OK;
}
#endif
}
// no match found
freeaddrinfo( addr_list );
return ESP_FAIL;
}

View File

@ -0,0 +1,515 @@
/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "protocol_examples_common.h"
#include "sdkconfig.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_default.h"
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
#include "esp_eth.h"
#if CONFIG_ETH_USE_SPI_ETHERNET
#include "driver/spi_master.h"
#endif // CONFIG_ETH_USE_SPI_ETHERNET
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
#include "esp_log.h"
#include "esp_netif.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
#define MAX_IP6_ADDRS_PER_NETIF (5)
#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces*2)
#if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_LINK_LOCAL
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_GLOBAL
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_SITE_LOCAL
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_UNIQUE_LOCAL
#endif // if-elif CONFIG_EXAMPLE_CONNECT_IPV6_PREF_...
#else
#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces)
#endif
#define EXAMPLE_DO_CONNECT CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET
#if CONFIG_EXAMPLE_WIFI_SCAN_METHOD_FAST
#define EXAMPLE_WIFI_SCAN_METHOD WIFI_FAST_SCAN
#elif CONFIG_EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
#define EXAMPLE_WIFI_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
#endif
#if CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
#elif CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY
#endif
#if CONFIG_EXAMPLE_WIFI_AUTH_OPEN
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_EXAMPLE_WIFI_AUTH_WEP
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_ENTERPRISE
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA3_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_EXAMPLE_WIFI_AUTH_WAPI_PSK
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif
static int s_active_interfaces = 0;
static SemaphoreHandle_t s_semph_get_ip_addrs;
static esp_netif_t *s_example_esp_netif = NULL;
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
static esp_ip6_addr_t s_ipv6_addr;
/* types of ipv6 addresses to be displayed on ipv6 events */
static const char *s_ipv6_addr_types[] = {
"ESP_IP6_ADDR_IS_UNKNOWN",
"ESP_IP6_ADDR_IS_GLOBAL",
"ESP_IP6_ADDR_IS_LINK_LOCAL",
"ESP_IP6_ADDR_IS_SITE_LOCAL",
"ESP_IP6_ADDR_IS_UNIQUE_LOCAL",
"ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6"
};
#endif
static const char *TAG = "example_connect";
#if CONFIG_EXAMPLE_CONNECT_WIFI
static esp_netif_t *wifi_start(void);
static void wifi_stop(void);
#endif
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
static esp_netif_t *eth_start(void);
static void eth_stop(void);
#endif
/**
* @brief Checks the netif description if it contains specified prefix.
* All netifs created withing common connect component are prefixed with the module TAG,
* so it returns true if the specified netif is owned by this module
*/
static bool is_our_netif(const char *prefix, esp_netif_t *netif)
{
return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0;
}
/* set up connection, Wi-Fi and/or Ethernet */
static void start(void)
{
#if CONFIG_EXAMPLE_CONNECT_WIFI
s_example_esp_netif = wifi_start();
s_active_interfaces++;
#endif
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
s_example_esp_netif = eth_start();
s_active_interfaces++;
#endif
#if CONFIG_EXAMPLE_CONNECT_WIFI && CONFIG_EXAMPLE_CONNECT_ETHERNET
/* if both intefaces at once, clear out to indicate that multiple netifs are active */
s_example_esp_netif = NULL;
#endif
#if EXAMPLE_DO_CONNECT
/* create semaphore if at least one interface is active */
s_semph_get_ip_addrs = xSemaphoreCreateCounting(NR_OF_IP_ADDRESSES_TO_WAIT_FOR, 0);
#endif
}
/* tear down connection, release resources */
static void stop(void)
{
#if CONFIG_EXAMPLE_CONNECT_WIFI
wifi_stop();
s_active_interfaces--;
#endif
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
eth_stop();
s_active_interfaces--;
#endif
}
#if EXAMPLE_DO_CONNECT
static esp_ip4_addr_t s_ip_addr;
static void on_got_ip(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
if (!is_our_netif(TAG, event->esp_netif)) {
ESP_LOGW(TAG, "Got IPv4 from another interface \"%s\": ignored", esp_netif_get_desc(event->esp_netif));
return;
}
ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr));
xSemaphoreGive(s_semph_get_ip_addrs);
}
#endif
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
static void on_got_ipv6(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
if (!is_our_netif(TAG, event->esp_netif)) {
ESP_LOGW(TAG, "Got IPv6 from another netif: ignored");
return;
}
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif),
IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]);
if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) {
memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr));
xSemaphoreGive(s_semph_get_ip_addrs);
}
}
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
esp_err_t example_connect(void)
{
#if EXAMPLE_DO_CONNECT
if (s_semph_get_ip_addrs != NULL) {
return ESP_ERR_INVALID_STATE;
}
#endif
start();
ESP_ERROR_CHECK(esp_register_shutdown_handler(&stop));
ESP_LOGI(TAG, "Waiting for IP(s)");
for (int i = 0; i < NR_OF_IP_ADDRESSES_TO_WAIT_FOR; ++i) {
xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY);
}
// iterate over active interfaces, and print out IPs of "our" netifs
esp_netif_t *netif = NULL;
esp_netif_ip_info_t ip;
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
netif = esp_netif_next(netif);
if (is_our_netif(TAG, netif)) {
ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif));
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
ESP_LOGI(TAG, "- IPv4 address: " IPSTR, IP2STR(&ip.ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
esp_ip6_addr_t ip6[MAX_IP6_ADDRS_PER_NETIF];
int ip6_addrs = esp_netif_get_all_ip6(netif, ip6);
for (int j = 0; j < ip6_addrs; ++j) {
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(ip6[j]), s_ipv6_addr_types[ipv6_type]);
}
#endif
}
}
return ESP_OK;
}
esp_err_t example_disconnect(void)
{
if (s_semph_get_ip_addrs == NULL) {
return ESP_ERR_INVALID_STATE;
}
vSemaphoreDelete(s_semph_get_ip_addrs);
s_semph_get_ip_addrs = NULL;
stop();
ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&stop));
return ESP_OK;
}
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
static void on_wifi_disconnect(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect...");
esp_err_t err = esp_wifi_connect();
if (err == ESP_ERR_WIFI_NOT_STARTED) {
return;
}
ESP_ERROR_CHECK(err);
}
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
static void on_wifi_connect(void *esp_netif, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
esp_netif_create_ip6_linklocal(esp_netif);
}
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
static esp_netif_t *wifi_start(void)
{
char *desc;
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
// Prefix the interface description with the module TAG
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
esp_netif_config.if_desc = desc;
esp_netif_config.route_prio = 128;
esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config);
free(desc);
esp_wifi_set_default_wifi_sta_handlers();
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect, netif));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL));
#endif
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_EXAMPLE_WIFI_SSID,
.password = CONFIG_EXAMPLE_WIFI_PASSWORD,
.scan_method = EXAMPLE_WIFI_SCAN_METHOD,
.sort_method = EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD,
.threshold.rssi = CONFIG_EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD,
.threshold.authmode = EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD,
},
};
ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
return netif;
}
static void wifi_stop(void)
{
esp_netif_t *wifi_netif = get_example_netif_from_desc("sta");
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect));
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect));
#endif
esp_err_t err = esp_wifi_stop();
if (err == ESP_ERR_WIFI_NOT_INIT) {
return;
}
ESP_ERROR_CHECK(err);
ESP_ERROR_CHECK(esp_wifi_deinit());
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif));
esp_netif_destroy(wifi_netif);
s_example_esp_netif = NULL;
}
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
/** Event handler for Ethernet events */
static void on_eth_event(void *esp_netif, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(esp_netif));
break;
default:
break;
}
}
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
static esp_eth_handle_t s_eth_handle = NULL;
static esp_eth_mac_t *s_mac = NULL;
static esp_eth_phy_t *s_phy = NULL;
static esp_eth_netif_glue_handle_t s_eth_glue = NULL;
static esp_netif_t *eth_start(void)
{
char *desc;
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
// Prefix the interface description with the module TAG
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
esp_netif_config.if_desc = desc;
esp_netif_config.route_prio = 64;
esp_netif_config_t netif_config = {
.base = &esp_netif_config,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
};
esp_netif_t *netif = esp_netif_new(&netif_config);
assert(netif);
free(desc);
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
s_phy = esp_eth_phy_new_ip101(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
s_phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX
s_phy = esp_eth_phy_new_lan87xx(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
s_phy = esp_eth_phy_new_dp83848(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX
s_phy = esp_eth_phy_new_ksz80xx(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
#if CONFIG_EXAMPLE_USE_DM9051
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
s_phy = esp_eth_phy_new_dm9051(&phy_config);
#elif CONFIG_EXAMPLE_USE_W5500
spi_device_interface_config_t devcfg = {
.command_bits = 16, // Actually it's the address phase in W5500 SPI frame
.address_bits = 8, // Actually it's the control phase in W5500 SPI frame
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
/* w5500 ethernet driver is based on spi driver */
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
w5500_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
s_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
s_phy = esp_eth_phy_new_w5500(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_OPENETH
phy_config.autonego_timeout_ms = 100;
s_mac = esp_eth_mac_new_openeth(&mac_config);
s_phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
// Install Ethernet driver
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
#if !CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
/* The SPI Ethernet module might doesn't have a burned factory MAC address, we cat to set it manually.
02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control.
*/
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_MAC_ADDR, (uint8_t[]) {
0x02, 0x00, 0x00, 0x12, 0x34, 0x56
}));
#endif
// combine driver with netif
s_eth_glue = esp_eth_new_netif_glue(s_eth_handle);
esp_netif_attach(netif, s_eth_glue);
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip, NULL));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event, netif));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL));
#endif
esp_eth_start(s_eth_handle);
return netif;
}
static void eth_stop(void)
{
esp_netif_t *eth_netif = get_example_netif_from_desc("eth");
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event));
#endif
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle));
s_eth_handle = NULL;
ESP_ERROR_CHECK(s_phy->del(s_phy));
ESP_ERROR_CHECK(s_mac->del(s_mac));
esp_netif_destroy(eth_netif);
s_example_esp_netif = NULL;
}
esp_eth_handle_t get_example_eth_handle(void)
{
return s_eth_handle;
}
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
esp_netif_t *get_example_netif(void)
{
return s_example_esp_netif;
}
esp_netif_t *get_example_netif_from_desc(const char *desc)
{
esp_netif_t *netif = NULL;
char *expected_desc;
asprintf(&expected_desc, "%s: %s", TAG, desc);
while ((netif = esp_netif_next(netif)) != NULL) {
if (strcmp(esp_netif_get_desc(netif), expected_desc) == 0) {
free(expected_desc);
return netif;
}
}
free(expected_desc);
return netif;
}

View File

@ -0,0 +1,44 @@
/* Common utilities for socket address input interface:
The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured).
This option is typically used in the CI, but could be enabled in the project configuration.
In that case this component is used to receive a string that is evaluated and processed to output
socket structures to open a connectio
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include <arpa/inet.h>
/**
* @brief Read and evaluate IP address from stdin
*
* This API reads stdin and parses the input address using getaddrinfo()
* to fill in struct sockaddr_storage (for both IPv4 and IPv6) used to open
* a socket. IP protocol is guessed from the IP address string.
*
* @param[in] port port number of expected connection
* @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM
* @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6
* @param[out] addr_family resultant address family: AF_INET or AF_INET6
* @param[out] dest_addr sockaddr_storage structure (for both IPv4 and IPv6)
* @return ESP_OK on success, ESP_FAIL otherwise
*/
esp_err_t get_addr_from_stdin(int port, int sock_type,
int *ip_protocol,
int *addr_family,
struct sockaddr_storage *dest_addr);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,92 @@
/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "esp_netif.h"
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
#define EXAMPLE_INTERFACE get_example_netif()
#endif
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
#define EXAMPLE_INTERFACE get_example_netif()
#endif
#if !defined (CONFIG_EXAMPLE_CONNECT_ETHERNET) && !defined (CONFIG_EXAMPLE_CONNECT_WIFI)
// This is useful for some tests which do not need a network connection
#define EXAMPLE_INTERFACE NULL
#endif
/**
* @brief Configure Wi-Fi or Ethernet, connect, wait for IP
*
* This all-in-one helper function is used in protocols examples to
* reduce the amount of boilerplate in the example.
*
* It is not intended to be used in real world applications.
* See examples under examples/wifi/getting_started/ and examples/ethernet/
* for more complete Wi-Fi or Ethernet initialization code.
*
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*
* @return ESP_OK on successful connection
*/
esp_err_t example_connect(void);
/**
* Counterpart to example_connect, de-initializes Wi-Fi or Ethernet
*/
esp_err_t example_disconnect(void);
/**
* @brief Configure stdin and stdout to use blocking I/O
*
* This helper function is used in ASIO examples. It wraps installing the
* UART driver and configuring VFS layer to use UART driver for console I/O.
*/
esp_err_t example_configure_stdin_stdout(void);
/**
* @brief Returns esp-netif pointer created by example_connect()
*
* @note If multiple interfaces active at once, this API return NULL
* In that case the get_example_netif_from_desc() should be used
* to get esp-netif pointer based on interface description
*/
esp_netif_t *get_example_netif(void);
/**
* @brief Returns esp-netif pointer created by example_connect() described by
* the supplied desc field
*
* @param desc Textual interface of created network interface, for example "sta"
* indicate default WiFi station, "eth" default Ethernet interface.
*
*/
esp_netif_t *get_example_netif_from_desc(const char *desc);
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
/**
* @brief Get the example Ethernet driver handle
*
* @return esp_eth_handle_t
*/
esp_eth_handle_t get_example_eth_handle(void);
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,29 @@
/* Common functions for protocol examples, to configure stdin and stdout.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "protocol_examples_common.h"
#include "esp_err.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "sdkconfig.h"
esp_err_t example_configure_stdin_stdout(void)
{
// Initialize VFS & UART so we can use std::cout/cin
setvbuf(stdin, NULL, _IONBF, 0);
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_ESP_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
return ESP_OK;
}

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,16 +34,11 @@ idf_component_register(SRCS "${srcs}"
PRIV_INCLUDE_DIRS private_include
REQUIRES ${dependencies})
# 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()
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
if(${target} STREQUAL "linux")
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64

View File

@ -16,6 +16,10 @@
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "network_dce.h"
#if ESP_IDF_VERSION_MAJOR >= 5
#include "esp_mac.h"
#include "dhcpserver/dhcpserver.h"
#endif
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD

View File

@ -5,5 +5,9 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(${COMPONENT_LIB} PRIVATE Threads::Threads)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")

View File

@ -4,5 +4,13 @@ cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "4.4")
if(("${IDF_TARGET}" STREQUAL "esp32s2") OR ("${IDF_TARGET}" STREQUAL "esp32s3"))
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common")
endif()
endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modem-console)

View File

@ -1,7 +1,5 @@
# Modem console example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example is mainly targets experimenting with a modem device, sending custom commands and switching to PPP mode using esp-console, command line API.
@ -16,6 +14,16 @@ that sets up the DCE based on a custom module implemented in the `my_module_dce.
`get_module_name()` method supplying a user defined name, but keeps all other commands the same as defined in the `GenericModule`
class.
### USB DTE support
For USB enabled targets (ESP32-S2 and ESP32-S3), it is possible to connect to the modem device via USB.
1. In menuconfig, navigate to `Example Configuration->Type of serial connection to the modem` and choose `USB`.
2. Connect the modem USB signals to pin 19 (DATA-) and 20 (DATA+) on your ESP chip.
USB example uses Quactel BG96 modem device.
### Supported IDF versions
This example is only supported from `v4.2`, due to support of the console repl mode.
This example is only supported from `v4.2`, due to support of the console repl mode.
USB example is supported from `v4.4`.

View File

@ -0,0 +1,19 @@
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
idf_build_get_property(target IDF_TARGET)
# USB is supported on S2 and S3 targets and IDF version >= 4.4
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "4.4")
if(("${target}" STREQUAL "esp32s2") OR ("${target}" STREQUAL "esp32s3"))
idf_component_register(SRCS "esp_modem_usb.cpp" "esp_modem_usb_api_target.cpp"
REQUIRES cdc_acm_host esp_modem
REQUIRED_IDF_TARGETS esp32s2 esp32s3
PRIV_INCLUDE_DIRS "private_include"
INCLUDE_DIRS "include")
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
)
endif()
endif()

View File

@ -0,0 +1,191 @@
// Copyright 2021 Espressif Systems (Shanghai) CO 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 "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_modem_config.h"
#include "esp_modem_usb_config.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "usb/usb_host.h"
#include "usb/cdc_acm_host.h"
#include "sdkconfig.h"
#include "usb_terminal.hpp"
static const char *TAG = "usb_terminal";
/**
* @brief USB Host task
*
* This task is created only if install_usb_host is set to true in DTE configuration.
* In case the user doesn't want in install USB Host driver here, he must install it before creating UsbTerminal object.
*
* @param arg Unused
*/
void usb_host_task(void *arg)
{
while (1) {
// Start handling system events
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
ESP_LOGD(TAG, "No more clients: clean up\n");
usb_host_device_free_all();
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGD(TAG, "All free: uninstall USB lib\n");
break;
}
}
// Clean up USB Host
vTaskDelay(10); // Short delay to allow clients clean-up
usb_host_lib_handle_events(0, NULL); // Make sure there are now pending events
usb_host_uninstall();
vTaskDelete(NULL);
}
namespace esp_modem {
class UsbTerminal : public Terminal, private CdcAcmDevice {
public:
explicit UsbTerminal(const esp_modem_dte_config *config)
{
const struct esp_modem_usb_term_config* usb_config = (struct esp_modem_usb_term_config*)(config->extension_config);
// Install USB Host driver
if (usb_config->install_usb_host) {
const usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};
throw_if_esp_fail(usb_host_install(&host_config), "USB Host install failed");
ESP_LOGD(TAG, "USB Host installed");
throw_if_false(pdTRUE == xTaskCreatePinnedToCore(usb_host_task, "usb_host", 4096, NULL, config->task_priority + 1, NULL, usb_config->xCoreID), "USB host task failed");
}
// Install CDC-ACM driver
const cdc_acm_host_driver_config_t esp_modem_cdc_acm_driver_config = {
.driver_task_stack_size = config->task_stack_size,
.driver_task_priority = config->task_priority,
.xCoreID = (BaseType_t)usb_config->xCoreID
};
// Silently continue of error: CDC-ACM driver might be already installed
cdc_acm_host_install(&esp_modem_cdc_acm_driver_config);
// Open CDC-ACM device
const cdc_acm_host_device_config_t esp_modem_cdc_acm_device_config = {
.connection_timeout_ms = usb_config->timeout_ms,
.out_buffer_size = config->dte_buffer_size,
.event_cb = handle_notif,
.data_cb = handle_rx,
.user_arg = this
};
if (usb_config->cdc_compliant) {
throw_if_esp_fail(this->CdcAcmDevice::open(usb_config->vid, usb_config->pid,
usb_config->interface_idx, &esp_modem_cdc_acm_device_config),
"USB Device open failed");
} else {
throw_if_esp_fail(this->CdcAcmDevice::open_vendor_specific(usb_config->vid, usb_config->pid,
usb_config->interface_idx, &esp_modem_cdc_acm_device_config),
"USB Device open failed");
}
};
~UsbTerminal()
{
this->CdcAcmDevice::close();
};
void start() override
{
return;
}
void stop() override
{
return;
}
int write(uint8_t *data, size_t len) override
{
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
if (this->CdcAcmDevice::tx_blocking(data, len) != ESP_OK) {
return -1;
}
return len;
}
int read(uint8_t *data, size_t len) override
{
// This function should never be called. UsbTerminal provides data through Terminal::on_read callback
ESP_LOGW(TAG, "Unexpected call to UsbTerminal::read function");
return -1;
}
private:
UsbTerminal() = delete;
UsbTerminal(const UsbTerminal &copy) = delete;
UsbTerminal &operator=(const UsbTerminal &copy) = delete;
bool operator== (const UsbTerminal &param) const = delete;
bool operator!= (const UsbTerminal &param) const = delete;
static void handle_rx(uint8_t *data, size_t data_len, void *user_arg)
{
ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_DEBUG);
UsbTerminal *this_terminal = static_cast<UsbTerminal *>(user_arg);
if (data_len > 0 && this_terminal->on_read) {
this_terminal->on_read(data, data_len);
} else {
ESP_LOGD(TAG, "Unhandled RX data");
}
}
static void handle_notif(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_host_dev_event_data_t *event, void *user_ctx)
{
UsbTerminal *this_terminal = static_cast<UsbTerminal *>(user_ctx);
switch (event->type) {
// Notifications like Ring, Rx Carrier indication or Network connection indication are not relevant for USB terminal
case CDC_ACM_HOST_NETWORK_CONNECTION:
case CDC_ACM_HOST_SERIAL_STATE:
break;
case CDC_ACM_HOST_DEVICE_DISCONNECTED:
ESP_LOGW(TAG, "USB terminal disconnected");
cdc_acm_host_close(cdc_hdl);
if (this_terminal->on_error) {
this_terminal->on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
}
break;
case CDC_ACM_HOST_ERROR:
ESP_LOGE(TAG, "Unexpected CDC-ACM error: %d.", event->data.error);
if (this_terminal->on_error) {
this_terminal->on_error(terminal_error::UNEXPECTED_CONTROL_FLOW);
}
break;
default:
abort();
}
};
};
std::unique_ptr<Terminal> create_usb_terminal(const esp_modem_dte_config *config)
{
TRY_CATCH_RET_NULL(
return std::make_unique<UsbTerminal>(config);
)
}
} // namespace esp_modem

View File

@ -0,0 +1,32 @@
// Copyright 2021-2022 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 "usb_terminal.hpp"
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_netif.hpp"
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
static const char *TAG = "modem_usb_api_target";
#endif
namespace esp_modem {
std::shared_ptr<DTE> create_usb_dte(const dte_config *config)
{
TRY_CATCH_RET_NULL(
auto term = create_usb_terminal(config);
return std::make_shared<DTE>(config, std::move(term));
)
}
}

View File

@ -0,0 +1,29 @@
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "cxx_include/esp_modem_dce.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
namespace esp_modem {
/**
* @brief Create USB DTE
*
* @param config DTE configuration
* @return shared ptr to DTE on success
* nullptr on failure (either due to insufficient memory or wrong dte configuration)
* if exceptions are disabled the API abort()'s on error
*/
std::shared_ptr<DTE> create_usb_dte(const dte_config *config);
}

View File

@ -0,0 +1,63 @@
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stdbool.h>
/**
* @brief USB configuration structure
* @see USB host CDC-ACM driver documentation for details about interfaces settings
*/
struct esp_modem_usb_term_config {
uint16_t vid; /*!< Vendor ID of the USB device */
uint16_t pid; /*!< Product ID of the USB device */
int interface_idx; /*!< USB Interface index that will be used */
uint32_t timeout_ms; /*!< Time for a USB modem to connect to USB host. 0 means wait forever. */
int xCoreID; /*!< Core affinity of created tasks: CDC-ACM driver task and optional USB Host task */
bool cdc_compliant; /*!< Treat the USB device as CDC-compliant. Read CDC-ACM driver documentation for more details */
bool install_usb_host; /*!< Flag whether USB Host driver should be installed */
};
/**
* @brief ESP Mode USB DTE Default Configuration
*
* @param[in] _usb_config esp_modem_usb_term_config configuration structure. Can be obtained by ESP_MODEM_DEFAULT_USB_CONFIG
*
*/
#define ESP_MODEM_DTE_DEFAULT_USB_CONFIG(_usb_config) \
{ \
.dte_buffer_size = 512, \
.task_stack_size = 4096, \
.task_priority = 5, \
.extension_config = &_usb_config \
}
/**
* @brief ESP Modem USB Default Configuration
*
* @param[in] _vid USB Vendor ID
* @param[in] _pid USB Product ID
* @see USB host CDC-ACM driver documentation for details about interfaces settings
*/
#define ESP_MODEM_DEFAULT_USB_CONFIG(_vid, _pid) \
{ \
.vid = _vid, \
.pid = _pid, \
.interface_idx = 0, \
.timeout_ms = 0, \
.xCoreID = 0, \
.cdc_compliant = false, \
.install_usb_host = true \
}

View File

@ -0,0 +1,51 @@
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
#include "sdkconfig.h"
#include "esp_log.h"
struct esp_modem_dte_config;
// Copy-pasted from esp_modem/private_include/exception_stub.hpp
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
#define TRY_CATCH_OR_DO(block, action) \
try { block \
} catch (std::bad_alloc& e) { \
ESP_LOGE(TAG, "Out of memory"); \
action; \
} catch (::esp_modem::esp_err_exception& e) { \
esp_err_t err = e.get_err_t(); \
ESP_LOGE(TAG, "%s: Exception caught with ESP err_code=%d", __func__, err); \
ESP_LOGE(TAG, "%s", e.what()); \
action; \
}
#define TRY_CATCH_RET_NULL(block) TRY_CATCH_OR_DO(block, return nullptr)
#else
#define TRY_CATCH_OR_DO(block, action) \
block
#define TRY_CATCH_RET_NULL(block) \
block
#endif
namespace esp_modem {
std::unique_ptr<Terminal> create_usb_terminal(const esp_modem_dte_config *config);
} // namespace esp_modem

View File

@ -2,4 +2,5 @@ idf_component_register(SRCS "modem_console_main.cpp"
"console_helper.cpp"
"httpget_handle.c"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash esp_modem esp_modem_usb_dte
INCLUDE_DIRS ".")

View File

@ -0,0 +1,17 @@
menu "Example Configuration"
choice EXAMPLE_SERIAL_CONFIG
prompt "Type of serial connection to the modem"
default EXAMPLE_SERIAL_CONFIG_UART
config EXAMPLE_SERIAL_CONFIG_UART
bool "UART"
help
Connect to modem via UART.
config EXAMPLE_SERIAL_CONFIG_USB
bool "USB"
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
help
Connect to modem via USB (CDC-ACM class). For IDF version >= 4.4.
endchoice
endmenu

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;
}
@ -105,4 +106,4 @@ void modem_console_register_http(void)
.argtable = &http_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&http_cmd));
}
}

View File

@ -18,6 +18,10 @@
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#if defined(CONFIG_USB_OTG_SUPPORTED)
#include "esp_modem_usb_config.h"
#include "cxx_include/esp_modem_usb_api.hpp"
#endif
#include "esp_log.h"
#include "console_helper.hpp"
#include "my_module_dce.hpp"
@ -53,13 +57,29 @@ extern "C" void app_main(void)
ESP_ERROR_CHECK(esp_event_loop_create_default());
// init the netif, DTE and DCE respectively
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(DEFAULT_APN);
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
assert(esp_netif);
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_UART)
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
auto uart_dte = create_uart_dte(&dte_config);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(DEFAULT_APN);
auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif);
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296); // VID and PID of BG96 modem
// BG96 modem implements Vendor Specific class, that is CDC-ACM like. Interface for AT commands has index no. 2.
usb_config.interface_idx = 2;
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_USB_CONFIG(usb_config);
ESP_LOGI(TAG, "Waiting for USB device connection...");
auto dte = create_usb_dte(&dte_config);
std::unique_ptr<DCE> dce = create_BG96_dce(&dce_config, dte, esp_netif);
#else
#error Invalid serial connection to modem.
#endif
assert(dce != nullptr);
// init console REPL environment

View File

@ -143,7 +143,21 @@ void app_main(void)
// Init netif object
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
assert(esp_netif);
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_BG96, &dte_config, &dce_config, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module...");
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM800, &dte_config, &dce_config, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM7600, &dte_config, &dce_config, esp_netif);
#else
ESP_LOGI(TAG, "Initializing esp_modem for a generic module...");
esp_modem_dce_t *dce = esp_modem_new(&dte_config, &dce_config, esp_netif);
#endif
assert(dce);
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
// check if PIN needed

View File

@ -70,9 +70,9 @@ extern "C" void app_main(void)
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
std::unique_ptr<DCE> dce = create_BG96_dce(&dce_config, dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
std::unique_ptr<DCE> dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
std::unique_ptr<DCE> dce = create_SIM800_dce(&dce_config, dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
std::unique_ptr<DCE> dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif);
std::unique_ptr<DCE> dce = create_SIM7600_dce(&dce_config, dte, esp_netif);
#else
#error "Unsupported device"
#endif

View File

@ -1,5 +1,6 @@
version: "0.1.11"
version: "0.1.17"
description: esp modem
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
dependencies:
# Required IDF version
idf:

View File

@ -82,6 +82,17 @@ std::shared_ptr<DTE> create_vfs_dte(const dte_config *config);
*/
std::unique_ptr<DCE> create_SIM7600_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create DCE based on SIM7070 module
*/
std::unique_ptr<DCE> create_SIM7070_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create DCE based on SIM7000 module
*/
std::unique_ptr<DCE> create_SIM7000_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create DCE based on SIM800 module
*/

View File

@ -44,7 +44,10 @@ DECLARE_ALL_COMMAND_APIS(declare name(Commandable *p, ...);)
* @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 set_gnss_power_mode_sim76xx(CommandableIf *t, int mode);
command_result power_down_sim76xx(CommandableIf *t);
command_result power_down_sim70xx(CommandableIf *t);
command_result set_network_bands_sim76xx(CommandableIf *t, const std::string& mode, const int* bands, int size);
command_result power_down_sim8xx(CommandableIf *t);
command_result set_data_mode_sim8xx(CommandableIf *t);

View File

@ -34,7 +34,7 @@ namespace esp_modem {
*/
class DCE_Mode {
public:
DCE_Mode(): mode(modem_mode::COMMAND_MODE) {}
DCE_Mode(): mode(modem_mode::UNDEF) {}
~DCE_Mode() = default;
bool set(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
modem_mode get();

View File

@ -117,6 +117,8 @@ private:
enum class ModemType {
GenericModule, /*!< Default generic module with the most common commands */
SIM7600, /*!< Derived from the GenericModule, specifics applied to SIM7600 model */
SIM7070, /*!< Derived from the GenericModule, specifics applied to SIM7070 model */
SIM7000, /*!< Derived from the GenericModule, specifics applied to SIM7000 model */
BG96, /*!< Derived from the GenericModule, specifics applied to BG69 model */
SIM800, /*!< Derived from the GenericModule with specifics applied to SIM800 model */
};
@ -173,6 +175,10 @@ public:
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::SIM7070:
return build_shared_module<SIM7070>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7000:
return build_shared_module<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_shared_module<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
@ -198,6 +204,10 @@ public:
return build_unique<SIM800>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7600:
return build_unique<SIM7600>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7070:
return build_unique<SIM7070>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7000:
return build_unique<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_unique<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
@ -216,6 +226,10 @@ public:
return build<SIM800>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7600:
return build<SIM7600>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7070:
return build<SIM7070>(cfg, std::forward<Args>(args)...);
case ModemType::SIM7000:
return build<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:

View File

@ -119,9 +119,28 @@ 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 power_down() override;
command_result set_gnss_power_mode(int mode) override;
command_result set_network_bands(const std::string& mode, const int* bands, int size) override;
};
/**
* @brief Specific definition of the SIM7070 module
*/
class SIM7070: public GenericModule {
using GenericModule::GenericModule;
public:
command_result power_down() override;
};
/**
* @brief Specific definition of the SIM7000 module
*/
class SIM7000: public GenericModule {
using GenericModule::GenericModule;
public:
command_result power_down() override;
};
/**
@ -130,7 +149,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;
};
@ -140,8 +158,6 @@ public:
*/
class BG96: public GenericModule {
using GenericModule::GenericModule;
public:
command_result get_module_name(std::string &name) override;
};
/**

View File

@ -22,9 +22,9 @@
#include <thread>
#else
#include "freertos/event_groups.h"
// forward declarations of FreeRTOS primitives
struct QueueDefinition;
typedef void *EventGroupHandle_t;
#endif

View File

@ -67,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
@ -83,6 +89,12 @@ 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)
* @return true on success

View File

@ -48,6 +48,8 @@ typedef enum esp_modem_dce_device
{
ESP_MODEM_DCE_GENETIC, /**< The most generic device */
ESP_MODEM_DCE_SIM7600,
ESP_MODEM_DCE_SIM7070,
ESP_MODEM_DCE_SIM7000,
ESP_MODEM_DCE_BG96,
ESP_MODEM_DCE_SIM800,
} esp_modem_dce_device_t;

View File

@ -83,10 +83,11 @@ struct esp_modem_vfs_term_config {
struct esp_modem_dte_config {
size_t dte_buffer_size; /*!< DTE buffer size */
uint32_t task_stack_size; /*!< Terminal task stack size */
int task_priority; /*!< Terminal task priority */
unsigned task_priority; /*!< Terminal task priority */
union {
struct esp_modem_uart_term_config uart_config; /*!< Configuration for UART Terminal */
struct esp_modem_vfs_term_config vfs_config; /*!< Configuration for VFS Terminal */
void *extension_config; /*!< Configuration for app specific Terminal */
};
};

View File

@ -28,6 +28,7 @@
#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 INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) struct_name& p1
#else
@ -36,6 +37,7 @@
#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 INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) struct struct_name* p1
#endif
@ -47,7 +49,7 @@
ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result, 0) \
/**
* @brief Reads the operator name
* @param[out] name module name
* @param[out] operator name
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, 1, STRING_OUT(p1, name)) \
@ -200,7 +202,88 @@ ESP_MODEM_DECLARE_DCE_COMMAND(reset, command_result, 0) \
* @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(p1, baud))
ESP_MODEM_DECLARE_DCE_COMMAND(set_baud, command_result, 1, INT_IN(p1, baud)) \
\
/**
* @brief Force an attempt to connect to a specific operator
* @param[in] mode mode of attempt
* mode=0 - automatic
* mode=1 - manual
* mode=2 - deregister
* mode=3 - set format for read operation
* mode=4 - manual with fallback to automatic
* @param[in] format what format the operator is given in
* format=0 - long format
* format=1 - short format
* format=2 - numeric
* @param[in] oper the operator to connect to
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_operator, command_result, 3, INT_IN(p1, mode), INT_IN(p2, format), STRING_IN(p3, oper)) \
\
/**
* @brief Attach or detach from the GPRS service
* @param[in] state 1-attach 0-detach
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_network_attachment_state, command_result, 1, INT_IN(p1, state)) \
\
/**
* @brief Get network attachment state
* @param[out] state 1-attached 0-detached
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_network_attachment_state, command_result, 1, INT_OUT(p1, state)) \
\
/**
* @brief What mode the radio should be set to
* @param[in] state state 1-full 0-minimum ...
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_radio_state, command_result, 1, INT_IN(p1, state)) \
\
/**
* @brief Get current radio state
* @param[out] state 1-full 0-minimum ...
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_radio_state, command_result, 1, INT_OUT(p1, state)) \
\
/**
* @brief Set network mode
* @param[in] mode preferred mode
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_network_mode, command_result, 1, INT_IN(p1, mode)) \
\
/**
* @brief Preferred network mode (CAT-M and/or NB-IoT)
* @param[in] mode preferred selection
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_preferred_mode, command_result, 1, INT_IN(p1, mode)) \
\
/**
* @brief Set network bands for CAT-M or NB-IoT
* @param[in] mode CAT-M or NB-IoT
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_network_bands, command_result, 3, STRING_IN(p1, mode), INTEGER_LIST_IN(p2, bands), INT_IN(p3, size)) \
\
/**
* @brief Show network system mode
* @param[out] mode current network mode
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_network_system_mode, command_result, 1, INT_OUT(p1, mode)) \
\
/**
* @brief GNSS power control
* @param[out] mode power mode (0 - off, 1 - on)
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_gnss_power_mode, command_result, 1, INT_IN(p1, mode)) \
\
#ifdef GENERATE_DOCS

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

@ -90,43 +90,6 @@ 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)

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

@ -30,13 +30,6 @@ 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)
{
@ -61,6 +54,16 @@ std::unique_ptr<DCE> create_SIM7600_dce(const dce_config *config, std::shared_pt
return create_modem_dce(dce_factory::ModemType::SIM7600, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_SIM7070_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::SIM7070, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_SIM7000_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::SIM7000, 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);

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

@ -27,6 +27,10 @@
#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;
@ -55,6 +59,10 @@ static inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t m
switch (module) {
case ESP_MODEM_DCE_SIM7600:
return esp_modem::dce_factory::ModemType::SIM7600;
case ESP_MODEM_DCE_SIM7070:
return esp_modem::dce_factory::ModemType::SIM7070;
case ESP_MODEM_DCE_SIM7000:
return esp_modem::dce_factory::ModemType::SIM7000;
case ESP_MODEM_DCE_BG96:
return esp_modem::dce_factory::ModemType::BG96;
case ESP_MODEM_DCE_SIM800:
@ -100,6 +108,14 @@ extern "C" void esp_modem_destroy(esp_modem_dce_t *dce_wrap)
}
}
extern "C" esp_err_t esp_modem_sync(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->sync());
}
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) {
@ -259,3 +275,100 @@ extern "C" esp_err_t esp_modem_power_down(esp_modem_dce_t *dce_wrap)
}
return command_response_to_esp_err(dce_wrap->dce->power_down());
}
extern "C" esp_err_t esp_modem_set_operator(esp_modem_dce_t *dce_wrap, int mode, int format, const char* oper)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string operator_str(oper);
return command_response_to_esp_err(dce_wrap->dce->set_operator(mode, format, operator_str));
}
extern "C" esp_err_t esp_modem_set_network_attachment_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_attachment_state(state));
}
extern "C" esp_err_t esp_modem_get_network_attachment_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_attachment_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_radio_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_radio_state(state));
}
extern "C" esp_err_t esp_modem_get_radio_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_radio_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_network_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_mode(mode));
}
extern "C" esp_err_t esp_modem_set_preferred_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_preferred_mode(mode));
}
extern "C" esp_err_t esp_modem_set_network_bands(esp_modem_dce_t *dce_wrap, const char* mode, const int* bands, int size)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string mode_str(mode);
return command_response_to_esp_err(dce_wrap->dce->set_network_bands(mode, bands, size));
}
extern "C" esp_err_t esp_modem_get_network_system_mode(esp_modem_dce_t *dce_wrap, int* p_mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int mode;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_system_mode(mode));
if (ret == ESP_OK) {
*p_mode = mode;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_gnss_power_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_gnss_power_mode(mode));
}

View File

@ -103,7 +103,7 @@ struct CMux::CMuxFrame {
void CMux::data_available(uint8_t *data, size_t len)
{
if (data && type == 0xFF && len > 0 && dlci > 0) {
if (data && (type&FT_UIH) == FT_UIH && len > 0 && dlci > 0) { // valid payload on a virtual term
int virtual_term = dlci - 1;
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
// Post partial data (or defragment to post on CMUX footer)
@ -187,10 +187,22 @@ bool CMux::on_header(CMuxFrame &frame)
}
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;
if ((frame_header[3] & 1) == 0) {
if (frame_header_offset + frame.len <= 4) {
frame_header_offset += frame.len;
return false; // need read more
}
payload_offset = std::min(frame.len, 5 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
payload_len = frame_header[4] << 7;
frame_header_offset += payload_offset - 1; // rewind frame_header back to hold only 6 bytes size
} else {
payload_len = 0;
frame_header_offset += payload_offset;
}
dlci = frame_header[1] >> 2;
type = frame_header[2];
payload_len = (frame_header[3] >> 1);
payload_len += (frame_header[3] >> 1);
frame.advance(payload_offset);
state = cmux_state::PAYLOAD;
return true;

View File

@ -121,12 +121,18 @@ command_result power_down(CommandableIf *t)
return generic_command(t, "AT+QPOWD=1\r", "POWERED DOWN", "ERROR", 1000);
}
command_result power_down_sim7xxx(CommandableIf *t)
command_result power_down_sim76xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CPOF\r", 1000);
}
command_result power_down_sim70xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
}
command_result power_down_sim8xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
@ -167,7 +173,7 @@ command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int
// Parsing +CBC: <bcs>,<bcl>,<voltage>
out = out.substr(pattern.size());
int pos, value, property = 0;
while ((pos = out.find(',') != std::string::npos)) {
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;
}
@ -269,7 +275,7 @@ command_result set_data_mode(CommandableIf *t)
command_result set_data_mode_sim8xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
return generic_command(t, "ATD*99#\r", "CONNECT", "ERROR", 5000);
}
command_result resume_data_mode(CommandableIf *t)
@ -399,4 +405,142 @@ command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
return command_result::OK;
}
command_result set_operator(CommandableIf *t, int mode, int format, const std::string& oper)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+COPS=" + std::to_string(mode) + "," + std::to_string(format) + ",\"" + oper + "\"\r", 90000);
}
command_result set_network_attachment_state(CommandableIf *t, int state)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CGATT=" + std::to_string(state) + "\r");
}
command_result get_network_attachment_state(CommandableIf *t, int &state)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CGATT?\r", out);
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CGATT: ";
constexpr int pos = pattern.size();
if (out.find(pattern) == std::string::npos) {
return command_result::FAIL;
}
if (std::from_chars(out.data() + pos, out.data() + out.size(), state).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}
command_result set_radio_state(CommandableIf *t, int state)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CFUN=" + std::to_string(state) + "\r");
}
command_result get_radio_state(CommandableIf *t, int &state)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CFUN?\r", out);
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CFUN: ";
constexpr int pos = pattern.size();
if (out.find(pattern) == std::string::npos) {
return command_result::FAIL;
}
if (std::from_chars(out.data() + pos, out.data() + out.size(), state).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}
command_result set_network_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CNMP=" + std::to_string(mode) + "\r");
}
command_result set_preferred_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CMNB=" + std::to_string(mode) + "\r");
}
command_result set_network_bands(CommandableIf *t, const std::string& mode, const int* bands, int size)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string band_string = "";
for (int i = 0; i<size-1; ++i){
band_string += std::to_string(bands[i]) + ",";
}
band_string += std::to_string(bands[size-1]);
return generic_command_common(t, "AT+CBANDCFG=\"" + mode + "\"," + band_string + "\r");
}
// mode is expected to be 64bit string (in hex)
// any_mode = "0xFFFFFFFF7FFFFFFF";
command_result set_network_bands_sim76xx(CommandableIf *t, const std::string& mode, const int* bands, int size)
{
ESP_LOGV(TAG, "%s", __func__ );
static const char *hexDigits = "0123456789ABCDEF";
uint64_t band_bits = 0;
int hex_len = 16;
std::string band_string(hex_len, '0');
for (int i = 0; i<size; ++i) {
// OR-operation to add bands
auto band = bands[i]-1; // Sim7600 has 0-indexed band selection (band 20 has to be shifted 19 places)
band_bits |= 1 << band;
}
for(int i=hex_len; i>0; i--){
band_string[i-1] = hexDigits[(band_bits >> ((hex_len-i)*4)) & 0xF];
}
return generic_command_common(t, "AT+CNBP=" + mode + ",0x" + band_string + "\r");
}
command_result get_network_system_mode(CommandableIf *t, int &mode)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string_view out;
auto ret = generic_get_string(t, "AT+CNSMOD?\r", out);
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CNSMOD: ";
int mode_pos = out.find(",") + 1; // Skip "<n>,"
if (out.find(pattern) == std::string::npos) {
return command_result::FAIL;
}
if (std::from_chars(out.data() + mode_pos, out.data() + out.size(), mode).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}
command_result set_gnss_power_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CGNSPWR=" + std::to_string(mode) + "\r");
}
command_result set_gnss_power_mode_sim76xx(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
return generic_command_common(t, "AT+CGPS=" + std::to_string(mode) + "\r");
}
} // esp_modem::dce_commands

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unistd.h>
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce.hpp"
#include "esp_log.h"
@ -63,7 +65,9 @@ bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) {
return false;
}
device->set_mode(modem_mode::CMUX_MODE);
device->set_mode(modem_mode::CMUX_MODE); // switch the device into CMUX mode
usleep(100'000); // some devices need a few ms to switch
if (!dte->set_mode(modem_mode::CMUX_MODE)) {
return false;
}

View File

@ -50,26 +50,34 @@ 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)
{
return dce_commands::get_battery_status_sim7xxx(dte.get(), voltage, bcs, bcl);
}
command_result SIM7600::power_down()
command_result SIM7600::set_network_bands(const std::string& mode, const int* bands, int size)
{
return dce_commands::power_down_sim7xxx(dte.get());
return dce_commands::set_network_bands_sim76xx(dte.get(), mode, bands, size);
}
command_result SIM800::get_module_name(std::string &name)
command_result SIM7600::set_gnss_power_mode(int mode)
{
name = "800L";
return command_result::OK;
return dce_commands::set_gnss_power_mode_sim76xx(dte.get(), mode);
}
command_result SIM7600::power_down()
{
return dce_commands::power_down_sim76xx(dte.get());
}
command_result SIM7070::power_down()
{
return dce_commands::power_down_sim70xx(dte.get());
}
command_result SIM7000::power_down()
{
return dce_commands::power_down_sim70xx(dte.get());
}
command_result SIM800::power_down()
@ -82,10 +90,4 @@ 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

@ -111,4 +111,4 @@ void Task::Delay(uint32_t ms)
vTaskDelay(pdMS_TO_TICKS(ms));
}
} // namespace esp_modem
} // namespace esp_modem

View File

@ -89,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

@ -12,4 +12,4 @@ project(host_modem_test)
idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_COMPILER_CXX_EXCEPTIONS")
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
target_link_options(${esp_modem} INTERFACE -fsanitize=address)
target_link_options(${esp_modem} INTERFACE -fsanitize=address -fsanitize=undefined)

View File

@ -6,5 +6,9 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(${COMPONENT_LIB} PRIVATE Threads::Threads)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")

View File

@ -15,6 +15,10 @@ void LoopbackTerm::stop()
int LoopbackTerm::write(uint8_t *data, size_t len)
{
if (inject_by) { // injection test: ignore what we write, but respond with injected data
auto ret = std::async(&LoopbackTerm::batch_read, this);
return len;
}
if (len > 2 && (data[len - 1] == '\r' || data[len - 1] == '+') ) { // Simple AT responder
std::string command((char *)data, len);
std::string response;
@ -28,8 +32,10 @@ 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" :
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";
@ -65,6 +71,8 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
int LoopbackTerm::read(uint8_t *data, size_t len)
{
size_t read_len = std::min(data_len, len);
if (inject_by && read_len > inject_by)
read_len = inject_by;
if (read_len) {
if (loopback_data.capacity() < len) {
loopback_data.reserve(len);
@ -76,8 +84,30 @@ int LoopbackTerm::read(uint8_t *data, size_t len)
return read_len;
}
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96) {}
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96), inject_by(0) {}
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false) {}
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false), inject_by(0) {}
int LoopbackTerm::inject(uint8_t *data, size_t len, size_t injected_by)
{
if (data == nullptr) {
inject_by = 0;
return 0;
}
loopback_data.resize(len);
memcpy(&loopback_data[0], data, len);
data_len = len;
inject_by = injected_by;
return len;
}
void LoopbackTerm::batch_read()
{
while (data_len > 0) {
on_read(nullptr, std::min(inject_by, data_len));
Task::Delay(1);
}
}
LoopbackTerm::~LoopbackTerm() = default;

View File

@ -12,6 +12,13 @@ public:
~LoopbackTerm() override;
/**
* @brief Inject user data to the terminal, to respond.
* inject_by defines batch sizes: the read callback is called multiple times
* with partial data of `inject_by` size
*/
int inject(uint8_t *data, size_t len, size_t inject_by);
void start() override;
void stop() override;
@ -24,10 +31,12 @@ private:
STARTED,
STOPPED
};
void batch_read();
status_t status;
SignalGroup signal;
std::vector<uint8_t> loopback_data;
size_t data_len;
bool pin_ok;
bool is_bg96;
size_t inject_by;
};

View File

@ -7,12 +7,27 @@
using namespace esp_modem;
TEST_CASE("Test polymorphic delete for custom device/dte", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>(true);
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
// Create custom device and DTE manually to check for a potential undefined behaviour
auto device = new GenericModule(std::move(dte), &dce_config);
device->power_down();
delete device;
auto custom_dte = new DTE(std::make_unique<LoopbackTerm>(false));
custom_dte->command("AT", nullptr, 0);
delete custom_dte;
}
TEST_CASE("DCE AT parser", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>(true);
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_BG96_dce(&dce_config, dte, &netif);
@ -24,7 +39,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 +52,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");
}
@ -109,6 +128,7 @@ TEST_CASE("DCE modes", "[esp_modem]")
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == false);
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
@ -135,3 +155,47 @@ TEST_CASE("DCE CMUX test", "[esp_modem]")
}, 1000);
CHECK(ret == command_result::OK);
}
TEST_CASE("Test CMUX protocol by injecting payloads", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto loopback = term.get();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MODE) == true);
const auto test_command = "Test\n";
// 1 byte payload size
uint8_t test_payload[] = {0xf9, 0x05, 0xff, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x0a, 0xbb, 0xf9 };
loopback->inject(&test_payload[0], sizeof(test_payload), 1);
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char *) data, len);
CHECK(response == test_command);
return command_result::OK;
}, 1000);
CHECK(ret == command_result::OK);
// 2 byte payload size
uint8_t long_payload[453] = { 0xf9, 0x05, 0xef, 0x7c, 0x03, 0x7e }; // header
long_payload[5] = 0x7e; // payload to validate
long_payload[449] = 0x7e;
long_payload[450] = '\n';
long_payload[451] = 0x53; // footer
long_payload[452] = 0xf9;
for (int i=0; i<5; ++i) {
// inject the whole payload (i=0) and then per 1,2,3,4 bytes (i)
loopback->inject(&long_payload[0], sizeof(long_payload), i==0?sizeof(long_payload):i);
auto ret = dce->command("ignore", [&](uint8_t *data, size_t len) {
CHECK(data[0] == 0x7e);
CHECK(data[len-2] == 0x7e);
CHECK(data[len-1] == '\n');
return command_result::OK;
}, 1000);
CHECK(ret == command_result::OK);
}
}

View File

@ -3,4 +3,8 @@ idf_component_register(SRCS "pppd_test.cpp"
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
REQUIRES esp_modem)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)

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.

View File

@ -0,0 +1,21 @@
from esp_docs.conf_docs import * # noqa: F403,F401
extensions += ['sphinx_copybutton',
# Needed as a trigger for running doxygen
'esp_docs.esp_extensions.dummy_build_system',
'esp_docs.esp_extensions.run_doxygen',
]
# link roles config
github_repo = 'espressif/esp-protocols'
# context used by sphinx_idf_theme
html_context['github_user'] = 'espressif'
html_context['github_repo'] = 'esp-docs'
# Extra options required by sphinx_idf_theme
project_slug = 'esp-idf' # >=5.0
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
idf_targets = ['esp32']
languages = ['en']

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses ../conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
try:
from conf_common import * # noqa: F403,F401
except ImportError:
import os
import sys
sys.path.insert(0, os.path.abspath('../'))
from conf_common import * # noqa: F403,F401
# General information about the project.
project = u'ESP-Protocols'
copyright = u'2016 - 2022, Espressif Systems (Shanghai) Co., Ltd'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'

View File

@ -0,0 +1,127 @@
ESP WebSocket Client
====================
Overview
--------
The ESP WebSocket client is an implementation of `WebSocket protocol client <https://tools.ietf.org/html/rfc6455>`_ for {IDF_TARGET_NAME}
Features
--------
* Supports WebSocket over TCP, TLS with mbedtls
* Easy to setup with URI
* Multiple instances (Multiple clients in one application)
Configuration
-------------
URI
^^^
- Supports ``ws``, ``wss`` schemes
- WebSocket samples:
- ``ws://echo.websocket.org``: WebSocket over TCP, default port 80
- ``wss://echo.websocket.org``: WebSocket over SSL, default port 443
Minimal configurations:
.. code:: c
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org",
};
The WebSocket client supports the use of both path and query in the URI. Sample:
.. code:: c
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org/connectionhandler?id=104",
};
If there are any options related to the URI in
:cpp:type:`esp_websocket_client_config_t`, the option defined by the URI will be
overridden. Sample:
.. code:: c
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://echo.websocket.org:123",
.port = 4567,
};
//WebSocket client will connect to websocket.org using port 4567
TLS
^^^
Configuration:
.. code:: c
const esp_websocket_client_config_t ws_cfg = {
.uri = "wss://echo.websocket.org",
.cert_pem = (const char *)websocket_org_pem_start,
};
.. note:: If you want to verify the server, then you need to provide a certificate in PEM format, and provide to ``cert_pem`` in :cpp:type:`websocket_client_config_t`. If no certficate is provided then the TLS connection will default to not requiring verification.
PEM certificate for this example could be extracted from an openssl `s_client` command connecting to websocket.org.
In case a host operating system has `openssl` and `sed` packages installed, one could execute the following command to download and save the root or intermediate root certificate to a file (Note for Windows users: Both Linux like environment or Windows native packages may be used).
```
echo "" | openssl s_client -showcerts -connect websocket.org:443 | sed -n "1,/Root/d; /BEGIN/,/END/p" | openssl x509 -outform PEM >websocket_org.pem
```
This command will extract the second certificate in the chain and save it as a pem-file.
Subprotocol
^^^^^^^^^^^
The subprotocol field in the config struct can be used to request a subprotocol
.. code:: c
const esp_websocket_client_config_t ws_cfg = {
.uri = "ws://websocket.org",
.subprotocol = "soap",
};
.. note:: The client is indifferent to the subprotocol field in the server response and will accept the connection no matter what the server replies.
For more options on :cpp:type:`esp_websocket_client_config_t`, please refer to API reference below
Events
------
* `WEBSOCKET_EVENT_CONNECTED`: The client has successfully established a connection to the server. The client is now ready to send and receive data. Contains no event data.
* `WEBSOCKET_EVENT_DISCONNECTED`: The client has aborted the connection due to the transport layer failing to read data, e.g. because the server is unavailable. Contains no event data.
* `WEBSOCKET_EVENT_DATA`: The client has successfully received and parsed a WebSocket frame. The event data contains a pointer to the payload data, the length of the payload data as well as the opcode of the received frame. A message may be fragmented into multiple events if the length exceeds the buffer size. This event will also be posted for non-payload frames, e.g. pong or connection close frames.
* `WEBSOCKET_EVENT_ERROR`: Not used in the current implementation of the client.
If the client handle is needed in the event handler it can be accessed through the pointer passed to the event handler:
.. code:: c
esp_websocket_client_handle_t client = (esp_websocket_client_handle_t)handler_args;
Limitations and Known Issues
----------------------------
* The client is able to request the use of a subprotocol from the server during the handshake, but does not do any subprotocol related checks on the response from the server.
Application Example
-------------------
A simple WebSocket example that uses esp_websocket_client to establish a websocket connection and send/receive data with the `websocket.org <https://websocket.org>`_ server can be found here: :example:`example <../examples>`.
Sending Text Data
^^^^^^^^^^^^^^^^^
The WebSocket client supports sending data as a text data frame, which informs the application layer that the payload data is text data encoded as UTF-8. Example:
.. code:: cpp
esp_websocket_client_send_text(client, data, len, portMAX_DELAY);
API Reference
-------------
.. include-build-file:: inc/esp_websocket_client.inc

View File

@ -0,0 +1,27 @@
build-docs --target esp32 --language en
cp -rf _build/en/esp32/html .
rm -rf _build __pycache__
# Modifes some version and target fields of index.html
echo "<script type="text/javascript">
window.onload =(function() {
var myAnchor = document.getElementById('version-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'latest';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>" >> html/index.html

View File

@ -0,0 +1,961 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "esp_websocket_client.h"
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_transport_ssl.h"
#include "esp_transport_ws.h"
/* using uri parser */
#include "http_parser.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_timer.h"
static const char *TAG = "WEBSOCKET_CLIENT";
#define WEBSOCKET_TCP_DEFAULT_PORT (80)
#define WEBSOCKET_SSL_DEFAULT_PORT (443)
#define WEBSOCKET_BUFFER_SIZE_BYTE (1024)
#define WEBSOCKET_RECONNECT_TIMEOUT_MS (10*1000)
#define WEBSOCKET_TASK_PRIORITY (5)
#define WEBSOCKET_TASK_STACK (4*1024)
#define WEBSOCKET_NETWORK_TIMEOUT_MS (10*1000)
#define WEBSOCKET_PING_INTERVAL_SEC (10)
#define WEBSOCKET_EVENT_QUEUE_SIZE (1)
#define WEBSOCKET_PINGPONG_TIMEOUT_SEC (120)
#define WEBSOCKET_KEEP_ALIVE_IDLE (5)
#define WEBSOCKET_KEEP_ALIVE_INTERVAL (5)
#define WEBSOCKET_KEEP_ALIVE_COUNT (3)
#define ESP_WS_CLIENT_MEM_CHECK(TAG, a, action) if (!(a)) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); \
action; \
}
#define ESP_WS_CLIENT_ERR_OK_CHECK(TAG, err, action) { \
esp_err_t _esp_ws_err_to_check = err; \
if (_esp_ws_err_to_check != ESP_OK) { \
ESP_LOGE(TAG,"%s(%d): Expected ESP_OK; reported: %d", __FUNCTION__, __LINE__, _esp_ws_err_to_check); \
action; \
} \
}
#define ESP_WS_CLIENT_STATE_CHECK(TAG, a, action) if ((a->state) < WEBSOCKET_STATE_INIT) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Websocket already stop"); \
action; \
}
const static int STOPPED_BIT = BIT0;
const static int CLOSE_FRAME_SENT_BIT = BIT1; // Indicates that a close frame was sent by the client
// and we are waiting for the server to continue with clean close
ESP_EVENT_DEFINE_BASE(WEBSOCKET_EVENTS);
typedef struct {
int task_stack;
int task_prio;
char *uri;
char *host;
char *path;
char *scheme;
char *username;
char *password;
int port;
bool auto_reconnect;
void *user_context;
int network_timeout_ms;
char *subprotocol;
char *user_agent;
char *headers;
int pingpong_timeout_sec;
size_t ping_interval_sec;
} websocket_config_storage_t;
typedef enum {
WEBSOCKET_STATE_ERROR = -1,
WEBSOCKET_STATE_UNKNOW = 0,
WEBSOCKET_STATE_INIT,
WEBSOCKET_STATE_CONNECTED,
WEBSOCKET_STATE_WAIT_TIMEOUT,
WEBSOCKET_STATE_CLOSING,
} websocket_client_state_t;
struct esp_websocket_client {
esp_event_loop_handle_t event_handle;
TaskHandle_t task_handle;
esp_transport_list_handle_t transport_list;
esp_transport_handle_t transport;
websocket_config_storage_t *config;
websocket_client_state_t state;
uint64_t keepalive_tick_ms;
uint64_t reconnect_tick_ms;
uint64_t ping_tick_ms;
uint64_t pingpong_tick_ms;
int wait_timeout_ms;
int auto_reconnect;
bool run;
bool wait_for_pong_resp;
EventGroupHandle_t status_bits;
SemaphoreHandle_t lock;
char *rx_buffer;
char *tx_buffer;
int buffer_size;
bool last_fin;
ws_transport_opcodes_t last_opcode;
int payload_len;
int payload_offset;
esp_transport_keep_alive_t keep_alive_cfg;
struct ifreq *if_name;
};
static uint64_t _tick_get_ms(void)
{
return esp_timer_get_time()/1000;
}
static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
const char *data,
int data_len)
{
esp_err_t err;
esp_websocket_event_data_t event_data;
event_data.client = client;
event_data.user_context = client->config->user_context;
event_data.data_ptr = data;
event_data.data_len = data_len;
event_data.fin = client->last_fin;
event_data.op_code = client->last_opcode;
event_data.payload_len = client->payload_len;
event_data.payload_offset = client->payload_offset;
if ((err = esp_event_post_to(client->event_handle,
WEBSOCKET_EVENTS, event,
&event_data,
sizeof(esp_websocket_event_data_t),
portMAX_DELAY)) != ESP_OK) {
return err;
}
return esp_event_loop_run(client->event_handle, 0);
}
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
if (client->config->auto_reconnect) {
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
}
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
return ESP_OK;
}
static esp_err_t esp_websocket_client_error_connection(esp_websocket_client_handle_t client)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
if (client->config->auto_reconnect) {
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
}
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_ERROR, NULL, 0);
return ESP_OK;
}
static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t client, const esp_websocket_client_config_t *config)
{
websocket_config_storage_t *cfg = client->config;
cfg->task_prio = config->task_prio;
if (cfg->task_prio <= 0) {
cfg->task_prio = WEBSOCKET_TASK_PRIORITY;
}
cfg->task_stack = config->task_stack;
if (cfg->task_stack == 0) {
cfg->task_stack = WEBSOCKET_TASK_STACK;
}
if (config->host) {
cfg->host = strdup(config->host);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->host, return ESP_ERR_NO_MEM);
}
if (config->port) {
cfg->port = config->port;
}
if (config->username) {
free(cfg->username);
cfg->username = strdup(config->username);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->username, return ESP_ERR_NO_MEM);
}
if (config->password) {
free(cfg->password);
cfg->password = strdup(config->password);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->password, return ESP_ERR_NO_MEM);
}
if (config->uri) {
free(cfg->uri);
cfg->uri = strdup(config->uri);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->uri, return ESP_ERR_NO_MEM);
}
if (config->path) {
free(cfg->path);
cfg->path = strdup(config->path);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->path, return ESP_ERR_NO_MEM);
}
if (config->subprotocol) {
free(cfg->subprotocol);
cfg->subprotocol = strdup(config->subprotocol);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->subprotocol, return ESP_ERR_NO_MEM);
}
if (config->user_agent) {
free(cfg->user_agent);
cfg->user_agent = strdup(config->user_agent);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->user_agent, return ESP_ERR_NO_MEM);
}
if (config->headers) {
free(cfg->headers);
cfg->headers = strdup(config->headers);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->headers, return ESP_ERR_NO_MEM);
}
cfg->user_context = config->user_context;
cfg->auto_reconnect = true;
if (config->disable_auto_reconnect) {
cfg->auto_reconnect = false;
}
if (config->disable_pingpong_discon){
cfg->pingpong_timeout_sec = 0;
} else if (config->pingpong_timeout_sec) {
cfg->pingpong_timeout_sec = config->pingpong_timeout_sec;
} else {
cfg->pingpong_timeout_sec = WEBSOCKET_PINGPONG_TIMEOUT_SEC;
}
if (config->network_timeout_ms <= 0) {
cfg->network_timeout_ms = WEBSOCKET_NETWORK_TIMEOUT_MS;
ESP_LOGW(TAG, "`network_timeout_ms` is not set, or it is less than or equal to zero, using default time out %d (milliseconds)", WEBSOCKET_NETWORK_TIMEOUT_MS);
} else {
cfg->network_timeout_ms = config->network_timeout_ms;
}
if (config->ping_interval_sec == 0) {
cfg->ping_interval_sec = WEBSOCKET_PING_INTERVAL_SEC;
} else {
cfg->ping_interval_sec = config->ping_interval_sec;
}
return ESP_OK;
}
static esp_err_t esp_websocket_client_destroy_config(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
websocket_config_storage_t *cfg = client->config;
if (client->config == NULL) {
return ESP_ERR_INVALID_ARG;
}
free(cfg->host);
free(cfg->uri);
free(cfg->path);
free(cfg->scheme);
free(cfg->username);
free(cfg->password);
free(cfg->subprotocol);
free(cfg->user_agent);
free(cfg->headers);
memset(cfg, 0, sizeof(websocket_config_storage_t));
free(client->config);
client->config = NULL;
return ESP_OK;
}
static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_handle_t client, const char *scheme)
{
esp_transport_handle_t trans = esp_transport_list_get_transport(client->transport_list, scheme);
if (trans) {
const esp_transport_ws_config_t config = {
.ws_path = client->config->path,
.sub_protocol = client->config->subprotocol,
.user_agent = client->config->user_agent,
.headers = client->config->headers,
.propagate_control_frames = true
};
return esp_transport_ws_set_config(trans, &config);
}
return ESP_ERR_INVALID_ARG;
}
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config)
{
esp_websocket_client_handle_t client = calloc(1, sizeof(struct esp_websocket_client));
ESP_WS_CLIENT_MEM_CHECK(TAG, client, return NULL);
esp_event_loop_args_t event_args = {
.queue_size = WEBSOCKET_EVENT_QUEUE_SIZE,
.task_name = NULL // no task will be created
};
if (esp_event_loop_create(&event_args, &client->event_handle) != ESP_OK) {
ESP_LOGE(TAG, "Error create event handler for websocket client");
free(client);
return NULL;
}
if (config->keep_alive_enable == true) {
client->keep_alive_cfg.keep_alive_enable = true;
client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? WEBSOCKET_KEEP_ALIVE_IDLE : config->keep_alive_idle;
client->keep_alive_cfg.keep_alive_interval = (config->keep_alive_interval == 0) ? WEBSOCKET_KEEP_ALIVE_INTERVAL : config->keep_alive_interval;
client->keep_alive_cfg.keep_alive_count = (config->keep_alive_count == 0) ? WEBSOCKET_KEEP_ALIVE_COUNT : config->keep_alive_count;
}
if (config->if_name) {
client->if_name = calloc(1, sizeof(struct ifreq) + 1);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->if_name, goto _websocket_init_fail);
memcpy(client->if_name, config->if_name, sizeof(struct ifreq));
}
client->lock = xSemaphoreCreateRecursiveMutex();
ESP_WS_CLIENT_MEM_CHECK(TAG, client->lock, goto _websocket_init_fail);
client->config = calloc(1, sizeof(websocket_config_storage_t));
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail);
client->transport_list = esp_transport_list_init();
ESP_WS_CLIENT_MEM_CHECK(TAG, client->transport_list, goto _websocket_init_fail);
esp_transport_handle_t tcp = esp_transport_tcp_init();
ESP_WS_CLIENT_MEM_CHECK(TAG, tcp, goto _websocket_init_fail);
esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT);
esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup
esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
esp_transport_tcp_set_interface_name(tcp, client->if_name);
esp_transport_handle_t ws = esp_transport_ws_init(tcp);
ESP_WS_CLIENT_MEM_CHECK(TAG, ws, goto _websocket_init_fail);
esp_transport_set_default_port(ws, WEBSOCKET_TCP_DEFAULT_PORT);
esp_transport_list_add(client->transport_list, ws, "ws");
if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) {
asprintf(&client->config->scheme, "ws");
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
}
esp_transport_handle_t ssl = esp_transport_ssl_init();
ESP_WS_CLIENT_MEM_CHECK(TAG, ssl, goto _websocket_init_fail);
esp_transport_set_default_port(ssl, WEBSOCKET_SSL_DEFAULT_PORT);
esp_transport_list_add(client->transport_list, ssl, "_ssl"); // need to save to transport list, for cleanup
if (config->use_global_ca_store == true) {
esp_transport_ssl_enable_global_ca_store(ssl);
} else if (config->cert_pem) {
if (!config->cert_len) {
esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem));
} else {
esp_transport_ssl_set_cert_data_der(ssl, config->cert_pem, config->cert_len);
}
}
if (config->client_cert) {
if (!config->client_cert_len) {
esp_transport_ssl_set_client_cert_data(ssl, config->client_cert, strlen(config->client_cert));
} else {
esp_transport_ssl_set_client_cert_data_der(ssl, config->client_cert, config->client_cert_len);
}
}
if (config->client_key) {
if (!config->client_key_len) {
esp_transport_ssl_set_client_key_data(ssl, config->client_key, strlen(config->client_key));
} else {
esp_transport_ssl_set_client_key_data_der(ssl, config->client_key, config->client_key_len);
}
}
if (config->skip_cert_common_name_check) {
esp_transport_ssl_skip_common_name_check(ssl);
}
if (config->reconnect_timeout_ms <= 0) {
client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS;
ESP_LOGW(TAG, "`reconnect_timeout_ms` is not set, or it is less than or equal to zero, using default time out %d (milliseconds)", WEBSOCKET_RECONNECT_TIMEOUT_MS);
} else {
client->wait_timeout_ms = config->reconnect_timeout_ms;
}
esp_transport_handle_t wss = esp_transport_ws_init(ssl);
ESP_WS_CLIENT_MEM_CHECK(TAG, wss, goto _websocket_init_fail);
esp_transport_set_default_port(wss, WEBSOCKET_SSL_DEFAULT_PORT);
esp_transport_list_add(client->transport_list, wss, "wss");
if (config->transport == WEBSOCKET_TRANSPORT_OVER_SSL) {
asprintf(&client->config->scheme, "wss");
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
}
if (config->uri) {
if (esp_websocket_client_set_uri(client, config->uri) != ESP_OK) {
ESP_LOGE(TAG, "Invalid uri");
goto _websocket_init_fail;
}
}
if (esp_websocket_client_set_config(client, config) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set the configuration");
goto _websocket_init_fail;
}
if (client->config->scheme == NULL) {
asprintf(&client->config->scheme, "ws");
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
}
ESP_WS_CLIENT_ERR_OK_CHECK(TAG, set_websocket_transport_optional_settings(client, "ws"), goto _websocket_init_fail;)
ESP_WS_CLIENT_ERR_OK_CHECK(TAG, set_websocket_transport_optional_settings(client, "wss"), goto _websocket_init_fail;)
client->keepalive_tick_ms = _tick_get_ms();
client->reconnect_tick_ms = _tick_get_ms();
client->ping_tick_ms = _tick_get_ms();
client->wait_for_pong_resp = false;
int buffer_size = config->buffer_size;
if (buffer_size <= 0) {
buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE;
}
client->rx_buffer = malloc(buffer_size);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->rx_buffer, {
goto _websocket_init_fail;
});
client->tx_buffer = malloc(buffer_size);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->tx_buffer, {
goto _websocket_init_fail;
});
client->status_bits = xEventGroupCreate();
ESP_WS_CLIENT_MEM_CHECK(TAG, client->status_bits, {
goto _websocket_init_fail;
});
client->buffer_size = buffer_size;
return client;
_websocket_init_fail:
esp_websocket_client_destroy(client);
return NULL;
}
esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (client->run) {
esp_websocket_client_stop(client);
}
if (client->event_handle) {
esp_event_loop_delete(client->event_handle);
}
if (client->if_name) {
free(client->if_name);
}
esp_websocket_client_destroy_config(client);
esp_transport_list_destroy(client->transport_list);
vQueueDelete(client->lock);
free(client->tx_buffer);
free(client->rx_buffer);
if (client->status_bits) {
vEventGroupDelete(client->status_bits);
}
free(client);
client = NULL;
return ESP_OK;
}
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri)
{
if (client == NULL || uri == NULL) {
return ESP_ERR_INVALID_ARG;
}
struct http_parser_url puri;
http_parser_url_init(&puri);
int parser_status = http_parser_parse_url(uri, strlen(uri), 0, &puri);
if (parser_status != 0) {
ESP_LOGE(TAG, "Error parse uri = %s", uri);
return ESP_FAIL;
}
if (puri.field_data[UF_SCHEMA].len) {
free(client->config->scheme);
asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, return ESP_ERR_NO_MEM);
}
if (puri.field_data[UF_HOST].len) {
free(client->config->host);
asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->host, return ESP_ERR_NO_MEM);
}
if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) {
free(client->config->path);
if (puri.field_data[UF_QUERY].len == 0) {
asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
} else if (puri.field_data[UF_PATH].len == 0) {
asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
} else {
asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->path, return ESP_ERR_NO_MEM);
}
if (puri.field_data[UF_PORT].off) {
client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10);
}
if (puri.field_data[UF_USERINFO].len) {
char *user_info = NULL;
asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off);
if (user_info) {
char *pass = strchr(user_info, ':');
if (pass) {
pass[0] = 0; //terminal username
pass ++;
free(client->config->password);
client->config->password = strdup(pass);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->password, return ESP_ERR_NO_MEM);
}
free(client->config->username);
client->config->username = strdup(user_info);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->username, return ESP_ERR_NO_MEM);
free(user_info);
} else {
return ESP_ERR_NO_MEM;
}
}
return ESP_OK;
}
static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
{
int rlen;
client->payload_offset = 0;
do {
rlen = esp_transport_read(client->transport, client->rx_buffer, client->buffer_size, client->config->network_timeout_ms);
if (rlen < 0) {
ESP_LOGE(TAG, "Error read data");
return ESP_FAIL;
}
client->payload_len = esp_transport_ws_get_read_payload_len(client->transport);
client->last_fin = esp_transport_ws_get_fin_flag(client->transport);
client->last_opcode = esp_transport_ws_get_read_opcode(client->transport);
if (rlen == 0 && client->last_opcode == WS_TRANSPORT_OPCODES_NONE ) {
ESP_LOGV(TAG, "esp_transport_read timeouts");
return ESP_OK;
}
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen);
client->payload_offset += rlen;
} while (client->payload_offset < client->payload_len);
// if a PING message received -> send out the PONG, this will not work for PING messages with payload longer than buffer len
if (client->last_opcode == WS_TRANSPORT_OPCODES_PING) {
const char *data = (client->payload_len == 0) ? NULL : client->rx_buffer;
ESP_LOGD(TAG, "Sending PONG with payload len=%d", client->payload_len);
esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PONG | WS_TRANSPORT_OPCODES_FIN, data, client->payload_len,
client->config->network_timeout_ms);
} else if (client->last_opcode == WS_TRANSPORT_OPCODES_PONG) {
client->wait_for_pong_resp = false;
} else if (client->last_opcode == WS_TRANSPORT_OPCODES_CLOSE) {
ESP_LOGD(TAG, "Received close frame");
client->state = WEBSOCKET_STATE_CLOSING;
}
return ESP_OK;
}
static int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout);
static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout);
static void esp_websocket_client_task(void *pv)
{
const int lock_timeout = portMAX_DELAY;
esp_websocket_client_handle_t client = (esp_websocket_client_handle_t) pv;
client->run = true;
//get transport by scheme
client->transport = esp_transport_list_get_transport(client->transport_list, client->config->scheme);
if (client->transport == NULL) {
ESP_LOGE(TAG, "There are no transports valid, stop websocket client");
client->run = false;
}
//default port
if (client->config->port == 0) {
client->config->port = esp_transport_get_default_port(client->transport);
}
client->state = WEBSOCKET_STATE_INIT;
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT);
int read_select = 0;
while (client->run) {
if (xSemaphoreTakeRecursive(client->lock, lock_timeout) != pdPASS) {
ESP_LOGE(TAG, "Failed to lock ws-client tasks, exiting the task...");
break;
}
switch ((int)client->state) {
case WEBSOCKET_STATE_INIT:
if (client->transport == NULL) {
ESP_LOGE(TAG, "There are no transport");
client->run = false;
break;
}
int result = esp_transport_connect(client->transport,
client->config->host,
client->config->port,
client->config->network_timeout_ms);
if (result < 0) {
ESP_LOGE(TAG, "Error transport connect %i", result);
esp_websocket_client_error_connection(client);
break;
}
ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port);
client->state = WEBSOCKET_STATE_CONNECTED;
client->wait_for_pong_resp = false;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0);
break;
case WEBSOCKET_STATE_CONNECTED:
if ((CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits)) == 0) { // only send and check for PING
// if closing hasn't been initiated
if (_tick_get_ms() - client->ping_tick_ms > client->config->ping_interval_sec*1000) {
client->ping_tick_ms = _tick_get_ms();
ESP_LOGD(TAG, "Sending PING...");
esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PING | WS_TRANSPORT_OPCODES_FIN, NULL, 0, client->config->network_timeout_ms);
if (!client->wait_for_pong_resp && client->config->pingpong_timeout_sec) {
client->pingpong_tick_ms = _tick_get_ms();
client->wait_for_pong_resp = true;
}
}
if ( _tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec*1000 ) {
if (client->wait_for_pong_resp) {
ESP_LOGE(TAG, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_abort_connection(client);
break;
}
}
}
if (read_select == 0) {
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
break;
}
client->ping_tick_ms = _tick_get_ms();
if (esp_websocket_client_recv(client) == ESP_FAIL) {
ESP_LOGE(TAG, "Error receive data");
esp_websocket_client_abort_connection(client);
break;
}
break;
case WEBSOCKET_STATE_WAIT_TIMEOUT:
if (!client->config->auto_reconnect) {
client->run = false;
break;
}
if (_tick_get_ms() - client->reconnect_tick_ms > client->wait_timeout_ms) {
client->state = WEBSOCKET_STATE_INIT;
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGD(TAG, "Reconnecting...");
}
break;
case WEBSOCKET_STATE_CLOSING:
// if closing not initiated by the client echo the close message back
if ((CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits)) == 0) {
ESP_LOGD(TAG, "Closing initiated by the server, sending close frame");
esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_CLOSE | WS_TRANSPORT_OPCODES_FIN, NULL, 0, client->config->network_timeout_ms);
xEventGroupSetBits(client->status_bits, CLOSE_FRAME_SENT_BIT);
}
break;
default:
ESP_LOGD(TAG, "Client run iteration in a default state: %d", client->state);
break;
}
xSemaphoreGiveRecursive(client->lock);
if (WEBSOCKET_STATE_CONNECTED == client->state) {
read_select = esp_transport_poll_read(client->transport, 1000); //Poll every 1000ms
if (read_select < 0) {
ESP_LOGE(TAG, "Network error: esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
esp_websocket_client_abort_connection(client);
}
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
// waiting for reconnecting...
vTaskDelay(client->wait_timeout_ms / 2 / portTICK_PERIOD_MS);
} else if (WEBSOCKET_STATE_CLOSING == client->state &&
(CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits))) {
ESP_LOGD(TAG, " Waiting for TCP connection to be closed by the server");
int ret = esp_transport_ws_poll_connection_closed(client->transport, 1000);
if (ret == 0) {
// still waiting
break;
}
if (ret < 0) {
ESP_LOGW(TAG, "Connection terminated while waiting for clean TCP close");
}
client->run = false;
client->state = WEBSOCKET_STATE_UNKNOW;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CLOSED, NULL, 0);
break;
}
}
esp_transport_close(client->transport);
xEventGroupSetBits(client->status_bits, STOPPED_BIT);
client->state = WEBSOCKET_STATE_UNKNOW;
vTaskDelete(NULL);
}
esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (client->state >= WEBSOCKET_STATE_INIT) {
ESP_LOGE(TAG, "The client has started");
return ESP_FAIL;
}
if (xTaskCreate(esp_websocket_client_task, "websocket_task", client->config->task_stack, client, client->config->task_prio, &client->task_handle) != pdTRUE) {
ESP_LOGE(TAG, "Error create websocket task");
return ESP_FAIL;
}
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT);
return ESP_OK;
}
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!client->run) {
ESP_LOGW(TAG, "Client was not started");
return ESP_FAIL;
}
/* A running client cannot be stopped from the websocket task/event handler */
TaskHandle_t running_task = xTaskGetCurrentTaskHandle();
if (running_task == client->task_handle) {
ESP_LOGE(TAG, "Client cannot be stopped from websocket task");
return ESP_FAIL;
}
client->run = false;
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
}
static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout)
{
uint8_t *close_status_data = NULL;
// RFC6455#section-5.5.1: The Close frame MAY contain a body (indicated by total_len >= 2)
if (total_len >= 2) {
close_status_data = calloc(1, total_len);
ESP_WS_CLIENT_MEM_CHECK(TAG, close_status_data, return -1);
// RFC6455#section-5.5.1: The first two bytes of the body MUST be a 2-byte representing a status
uint16_t *code_network_order = (uint16_t *) close_status_data;
*code_network_order = htons(code);
memcpy(close_status_data + 2, additional_data, total_len - 2);
}
int ret = esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_CLOSE, close_status_data, total_len, timeout);
free(close_status_data);
return ret;
}
static esp_err_t esp_websocket_client_close_with_optional_body(esp_websocket_client_handle_t client, bool send_body, int code, const char *data, int len, TickType_t timeout)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!client->run) {
ESP_LOGW(TAG, "Client was not started");
return ESP_FAIL;
}
/* A running client cannot be stopped from the websocket task/event handler */
TaskHandle_t running_task = xTaskGetCurrentTaskHandle();
if (running_task == client->task_handle) {
ESP_LOGE(TAG, "Client cannot be stopped from websocket task");
return ESP_FAIL;
}
if (send_body) {
esp_websocket_client_send_close(client, code, data, len + 2, portMAX_DELAY); // len + 2 -> always sending the code
} else {
esp_websocket_client_send_close(client, 0, NULL, 0, portMAX_DELAY); // only opcode frame
}
// Set closing bit to prevent from sending PING frames while connected
xEventGroupSetBits(client->status_bits, CLOSE_FRAME_SENT_BIT);
if (STOPPED_BIT & xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, timeout)) {
return ESP_OK;
}
// If could not close gracefully within timeout, stop the client and disconnect
client->run = false;
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
}
esp_err_t esp_websocket_client_close_with_code(esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout)
{
return esp_websocket_client_close_with_optional_body(client, true, code, data, len, timeout);
}
esp_err_t esp_websocket_client_close(esp_websocket_client_handle_t client, TickType_t timeout)
{
return esp_websocket_client_close_with_optional_body(client, false, 0, NULL, 0, timeout);
}
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
{
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, (const uint8_t *)data, len, timeout);
}
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
{
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, (const uint8_t *)data, len, timeout);
}
static int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
{
int need_write = len;
int wlen = 0, widx = 0;
int ret = ESP_FAIL;
if (client == NULL || len < 0 ||
(opcode != WS_TRANSPORT_OPCODES_CLOSE && (data == NULL || len <= 0))) {
ESP_LOGE(TAG, "Invalid arguments");
return ESP_FAIL;
}
if (xSemaphoreTakeRecursive(client->lock, timeout) != pdPASS) {
ESP_LOGE(TAG, "Could not lock ws-client within %d timeout", timeout);
return ESP_FAIL;
}
if (!esp_websocket_client_is_connected(client)) {
ESP_LOGE(TAG, "Websocket client is not connected");
goto unlock_and_return;
}
if (client->transport == NULL) {
ESP_LOGE(TAG, "Invalid transport");
goto unlock_and_return;
}
uint32_t current_opcode = opcode;
while (widx < len || current_opcode) { // allow for sending "current_opcode" only message with len==0
if (need_write > client->buffer_size) {
need_write = client->buffer_size;
} else {
current_opcode |= WS_TRANSPORT_OPCODES_FIN;
}
memcpy(client->tx_buffer, data + widx, need_write);
// send with ws specific way and specific opcode
wlen = esp_transport_ws_send_raw(client->transport, current_opcode, (char *)client->tx_buffer, need_write,
(timeout==portMAX_DELAY)? -1 : timeout * portTICK_PERIOD_MS);
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
ret = wlen;
ESP_LOGE(TAG, "Network error: esp_transport_write() returned %d, errno=%d", ret, errno);
esp_websocket_client_abort_connection(client);
goto unlock_and_return;
}
current_opcode = 0;
widx += wlen;
need_write = len - widx;
}
ret = widx;
unlock_and_return:
xSemaphoreGiveRecursive(client->lock);
return ret;
}
bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return false;
}
return client->state == WEBSOCKET_STATE_CONNECTED;
}
size_t esp_websocket_client_get_ping_interval_sec(esp_websocket_client_handle_t client)
{
if (client == NULL) {
ESP_LOGW(TAG, "Client was not initialized");
return 0;
}
if (client->config == NULL) {
ESP_LOGW(TAG, "No config available to change the ping interval");
return 0;
}
return client->config->ping_interval_sec;
}
esp_err_t esp_websocket_client_set_ping_interval_sec(esp_websocket_client_handle_t client, size_t ping_interval_sec)
{
if (client == NULL) {
ESP_LOGW(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
if (client->config == NULL) {
ESP_LOGW(TAG, "No config available to change the ping interval");
return ESP_ERR_INVALID_STATE;
}
client->config->ping_interval_sec = ping_interval_sec == 0 ? WEBSOCKET_PING_INTERVAL_SEC : ping_interval_sec;
return ESP_OK;
}
esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
esp_event_handler_t event_handler,
void *event_handler_arg)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
return esp_event_handler_register_with(client->event_handle, WEBSOCKET_EVENTS, event, event_handler, event_handler_arg);
}

View File

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(websocket_example)

View File

@ -0,0 +1,89 @@
# Websocket Sample application
This example will shows how to set up and communicate over a websocket.
## How to Use Example
### Hardware Required
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet or a local server.
### Configure the project
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu.
* Configure the websocket endpoint URI under "Example Configuration", if "WEBSOCKET_URI_FROM_STDIN" is selected then the example application will connect to the URI it reads from stdin (used for testing)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (482) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (2492) example_connect: Ethernet Link Up
I (4472) tcpip_adapter: eth ip: 192.168.2.137, mask: 255.255.255.0, gw: 192.168.2.2
I (4472) example_connect: Connected to Ethernet
I (4472) example_connect: IPv4 address: 192.168.2.137
I (4472) example_connect: IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed4:a92b
I (4482) WEBSOCKET: Connecting to ws://echo.websocket.events...
I (5012) WEBSOCKET: WEBSOCKET_EVENT_CONNECTED
I (5492) WEBSOCKET: Sending hello 0000
I (6052) WEBSOCKET: WEBSOCKET_EVENT_DATA
W (6052) WEBSOCKET: Received=hello 0000
I (6492) WEBSOCKET: Sending hello 0001
I (7052) WEBSOCKET: WEBSOCKET_EVENT_DATA
W (7052) WEBSOCKET: Received=hello 0001
I (7492) WEBSOCKET: Sending hello 0002
I (8082) WEBSOCKET: WEBSOCKET_EVENT_DATA
W (8082) WEBSOCKET: Received=hello 0002
I (8492) WEBSOCKET: Sending hello 0003
I (9152) WEBSOCKET: WEBSOCKET_EVENT_DATA
W (9162) WEBSOCKET: Received=hello 0003
```
## Python Flask echo server
By default, the `ws://echo.websocket.events` endpoint is used. You can setup a Python websocket echo server locally and try the `ws://<your-ip>:5000` endpoint. To do this, install Flask-sock Python package
```
pip install flask-sock
```
and start a Flask websocket echo server locally by executing the following Python code:
```python
from flask import Flask
from flask_sock import Sock
app = Flask(__name__)
sock = Sock(app)
@sock.route('/')
def echo(ws):
while True:
data = ws.receive()
ws.send(data)
if __name__ == '__main__':
# To run your Flask + WebSocket server in production you can use Gunicorn:
# gunicorn -b 0.0.0.0:5000 --workers 4 --threads 100 module:app
app.run(host="0.0.0.0", debug=True)
```

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "websocket_example.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,23 @@
menu "Example Configuration"
choice WEBSOCKET_URI_SOURCE
prompt "Websocket URI source"
default WEBSOCKET_URI_FROM_STRING
help
Selects the source of the URI used in the example.
config WEBSOCKET_URI_FROM_STRING
bool "From string"
config WEBSOCKET_URI_FROM_STDIN
bool "From stdin"
endchoice
config WEBSOCKET_URI
string "Websocket endpoint URI"
depends on WEBSOCKET_URI_FROM_STRING
default "ws://echo.websocket.events"
help
URL of websocket endpoint this example connects to and sends echo
endmenu

View File

@ -0,0 +1,153 @@
/* ESP HTTP Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_websocket_client.h"
#include "esp_event.h"
#define NO_DATA_TIMEOUT_SEC 5
static const char *TAG = "WEBSOCKET";
static TimerHandle_t shutdown_signal_timer;
static SemaphoreHandle_t shutdown_sema;
static void shutdown_signaler(TimerHandle_t xTimer)
{
ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC);
xSemaphoreGive(shutdown_sema);
}
#if CONFIG_WEBSOCKET_URI_FROM_STDIN
static void get_string(char *line, size_t size)
{
int count = 0;
while (count < size) {
int c = fgetc(stdin);
if (c == '\n') {
line[count] = '\0';
break;
} else if (c > 0 && c < 127) {
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
break;
case WEBSOCKET_EVENT_DATA:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
ESP_LOGI(TAG, "Received opcode=%d", data->op_code);
if (data->op_code == 0x08 && data->data_len == 2) {
ESP_LOGW(TAG, "Received closed message with code=%d", 256*data->data_ptr[0] + data->data_ptr[1]);
} else {
ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr);
}
ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset);
xTimerReset(shutdown_signal_timer, portMAX_DELAY);
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
break;
}
}
static void websocket_app_start(void)
{
esp_websocket_client_config_t websocket_cfg = {};
shutdown_signal_timer = xTimerCreate("Websocket shutdown timer", NO_DATA_TIMEOUT_SEC * 1000 / portTICK_PERIOD_MS,
pdFALSE, NULL, shutdown_signaler);
shutdown_sema = xSemaphoreCreateBinary();
#if CONFIG_WEBSOCKET_URI_FROM_STDIN
char line[128];
ESP_LOGI(TAG, "Please enter uri of websocket endpoint");
get_string(line, sizeof(line));
websocket_cfg.uri = line;
ESP_LOGI(TAG, "Endpoint uri: %s\n", line);
#else
websocket_cfg.uri = CONFIG_WEBSOCKET_URI;
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client);
esp_websocket_client_start(client);
xTimerStart(shutdown_signal_timer, portMAX_DELAY);
char data[32];
int i = 0;
while (i < 5) {
if (esp_websocket_client_is_connected(client)) {
int len = sprintf(data, "hello %04d", i++);
ESP_LOGI(TAG, "Sending %s", data);
esp_websocket_client_send_text(client, data, len, portMAX_DELAY);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
xSemaphoreTake(shutdown_sema, portMAX_DELAY);
esp_websocket_client_close(client, portMAX_DELAY);
ESP_LOGI(TAG, "Websocket Stopped");
esp_websocket_client_destroy(client);
}
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("WEBSOCKET_CLIENT", ESP_LOG_DEBUG);
esp_log_level_set("TRANSPORT_WS", ESP_LOG_DEBUG);
esp_log_level_set("TRANS_TCP", ESP_LOG_DEBUG);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
websocket_app_start();
}

View File

@ -0,0 +1,24 @@
[pytest]
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
python_files = pytest_*.py
addopts =
-s
--embedded-services esp,idf
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
--tb short
# ignore DeprecationWarning
filterwarnings =
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
# log related
log_cli = True
log_cli_level = INFO
log_cli_format = %(asctime)s %(levelname)s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = test.log
log_file_level = INFO
log_file_format = %(asctime)s %(levelname)s %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S

View File

@ -0,0 +1,128 @@
import os
import random
import re
import socket
import string
from threading import Event, Thread
import pytest
import sys
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
from pytest_embedded import Dut
def get_my_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('8.8.8.8', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
class WebsocketTestEcho(WebSocket):
def handleMessage(self):
self.sendMessage(self.data)
print('Server sent: {}'.format(self.data))
def handleConnected(self):
print('Connection from: {}'.format(self.address))
def handleClose(self):
print('{} closed the connection'.format(self.address))
# Simple Websocket server for testing purposes
class Websocket(object):
def send_data(self, data):
for nr, conn in self.server.connections.items():
conn.sendMessage(data)
def run(self):
self.server = SimpleWebSocketServer('', self.port, WebsocketTestEcho)
while not self.exit_event.is_set():
self.server.serveonce()
def __init__(self, port):
self.port = port
self.exit_event = Event()
self.thread = Thread(target=self.run)
self.thread.start()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.exit_event.set()
self.thread.join(10)
if self.thread.is_alive():
Utility.console_log('Thread cannot be joined', 'orange')
def test_examples_protocol_websocket(dut):
"""
steps:
1. obtain IP address
2. connect to uri specified in the config
3. send and receive data
"""
def test_echo(dut):
dut.expect('WEBSOCKET_EVENT_CONNECTED')
for i in range(0, 5):
dut.expect(re.compile(b'Received=hello (\\d)'))
print('All echos received')
def test_close(dut):
code = dut.expect(re.compile(b'WEBSOCKET: Received closed message with code=(\\d*)'))[0]
print('Received close frame with code {}'.format(code))
def test_recv_long_msg(dut, websocket, msg_len, repeats):
send_msg = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(msg_len))
for _ in range(repeats):
websocket.send_data(send_msg)
recv_msg = ''
while len(recv_msg) < msg_len:
match = dut.expect(re.compile(b'Received=([a-zA-Z0-9]*).*')).group(1).decode()
recv_msg += match
if recv_msg == send_msg:
print('Sent message and received message are equal')
else:
raise ValueError('DUT received string do not match sent string, \nexpected: {}\nwith length {}\
\nreceived: {}\nwith length {}'.format(send_msg, len(send_msg), recv_msg, len(recv_msg)))
# Starting of the test
try:
if dut.app.sdkconfig.get('WEBSOCKET_URI_FROM_STDIN') is True:
uri_from_stdin = True
else:
uri = dut.app.sdkconfig['WEBSOCKET_URI']
uri_from_stdin = False
except Exception:
print('ENV_TEST_FAILURE: Cannot find uri settings in sdkconfig')
raise
if uri_from_stdin:
server_port = 8080
with Websocket(server_port) as ws:
uri = 'ws://{}:{}'.format(get_my_ip(), server_port)
print('DUT connecting to {}'.format(uri))
dut.expect('Please enter uri of websocket endpoint', timeout=30)
dut.write(uri)
test_echo(dut)
# Message length should exceed DUT's buffer size to test fragmentation, default is 1024 byte
test_recv_long_msg(dut, ws, 2000, 3)
test_close(dut)
else:
print('DUT connecting to {}'.format(uri))
test_echo(dut)

View File

@ -0,0 +1,4 @@
pytest-embedded-serial-esp
pytest-embedded-idf
junit_xml
SimpleWebSocketServer

View File

@ -0,0 +1,12 @@
CONFIG_WEBSOCKET_URI_FROM_STDIN=n
CONFIG_WEBSOCKET_URI_FROM_STRING=y
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y

View File

@ -0,0 +1,5 @@
version: "0.0.1"
description: esp websocket client
dependencies:
idf:
version: ">=5.0"

View File

@ -0,0 +1,279 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_WEBSOCKET_CLIENT_H_
#define _ESP_WEBSOCKET_CLIENT_H_
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "esp_event.h"
#include <sys/socket.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_websocket_client *esp_websocket_client_handle_t;
ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task events family
/**
* @brief Websocket Client events id
*/
typedef enum {
WEBSOCKET_EVENT_ANY = -1,
WEBSOCKET_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */
WEBSOCKET_EVENT_CONNECTED, /*!< Once the Websocket has been connected to the server, no data exchange has been performed */
WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */
WEBSOCKET_EVENT_CLOSED, /*!< The connection has been closed cleanly */
WEBSOCKET_EVENT_MAX
} esp_websocket_event_id_t;
/**
* @brief Websocket event data
*/
typedef struct {
const char *data_ptr; /*!< Data pointer */
int data_len; /*!< Data length */
bool fin; /*!< Fin flag */
uint8_t op_code; /*!< Received opcode */
esp_websocket_client_handle_t client; /*!< esp_websocket_client_handle_t context */
void *user_context; /*!< user_data context, from esp_websocket_client_config_t user_data */
int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */
int payload_offset; /*!< Actual offset for the data associated with this event */
} esp_websocket_event_data_t;
/**
* @brief Websocket Client transport
*/
typedef enum {
WEBSOCKET_TRANSPORT_UNKNOWN = 0x0, /*!< Transport unknown */
WEBSOCKET_TRANSPORT_OVER_TCP, /*!< Transport over tcp */
WEBSOCKET_TRANSPORT_OVER_SSL, /*!< Transport over ssl */
} esp_websocket_transport_t;
/**
* @brief Websocket client setup configuration
*/
typedef struct {
const char *uri; /*!< Websocket URI, the information on the URI can be overrides the other fields below, if any */
const char *host; /*!< Domain or IP as string */
int port; /*!< Port to connect, default depend on esp_websocket_transport_t (80 or 443) */
const char *username; /*!< Using for Http authentication - Not supported for now */
const char *password; /*!< Using for Http authentication - Not supported for now */
const char *path; /*!< HTTP Path, if not set, default is `/` */
bool disable_auto_reconnect; /*!< Disable the automatic reconnect function when disconnected */
void *user_context; /*!< HTTP user data context */
int task_prio; /*!< Websocket task priority */
int task_stack; /*!< Websocket task stack */
int buffer_size; /*!< Websocket buffer size */
const char *cert_pem; /*!< Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len. */
size_t cert_len; /*!< Length of the buffer pointed to by cert_pem. May be 0 for null-terminated pem */
const char *client_cert; /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */
size_t client_cert_len; /*!< Length of the buffer pointed to by client_cert. May be 0 for null-terminated pem */
const char *client_key; /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */
size_t client_key_len; /*!< Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem */
esp_websocket_transport_t transport; /*!< Websocket transport type, see `esp_websocket_transport_t */
const char *subprotocol; /*!< Websocket subprotocol */
const char *user_agent; /*!< Websocket user-agent */
const char *headers; /*!< Websocket additional headers */
int pingpong_timeout_sec; /*!< Period before connection is aborted due to no PONGs received */
bool disable_pingpong_discon; /*!< Disable auto-disconnect due to no PONG received within pingpong_timeout_sec */
bool use_global_ca_store; /*!< Use a global ca_store for all the connections in which this bool is set. */
bool skip_cert_common_name_check;/*!< Skip any validation of server certificate CN field */
bool keep_alive_enable; /*!< Enable keep-alive timeout */
int keep_alive_idle; /*!< Keep-alive idle time. Default is 5 (second) */
int keep_alive_interval; /*!< Keep-alive interval time. Default is 5 (second) */
int keep_alive_count; /*!< Keep-alive packet retry send count. Default is 3 counts */
int reconnect_timeout_ms; /*!< Reconnect after this value in miliseconds if disable_auto_reconnect is not enabled (defaults to 10s) */
int network_timeout_ms; /*!< Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s) */
size_t ping_interval_sec; /*!< Websocket ping interval, defaults to 10 seconds if not set */
struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */
} esp_websocket_client_config_t;
/**
* @brief Start a Websocket session
* This function must be the first function to call,
* and it returns a esp_websocket_client_handle_t that you must use as input to other functions in the interface.
* This call MUST have a corresponding call to esp_websocket_client_destroy when the operation is complete.
*
* @param[in] config The configuration
*
* @return
* - `esp_websocket_client_handle_t`
* - NULL if any errors
*/
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config);
/**
* @brief Set URL for client, when performing this behavior, the options in the URL will replace the old ones
* Must stop the WebSocket client before set URI if the client has been connected
*
* @param[in] client The client
* @param[in] uri The uri
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri);
/**
* @brief Open the WebSocket connection
*
* @param[in] client The client
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client);
/**
* @brief Stops the WebSocket connection without websocket closing handshake
*
* This API stops ws client and closes TCP connection directly without sending
* close frames. It is a good practice to close the connection in a clean way
* using esp_websocket_client_close().
*
* Notes:
* - Cannot be called from the websocket event handler
*
* @param[in] client The client
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client);
/**
* @brief Destroy the WebSocket connection and free all resources.
* This function must be the last function to call for an session.
* It is the opposite of the esp_websocket_client_init function and must be called with the same handle as input that a esp_websocket_client_init call returned.
* This might close all connections this handle has used.
*
* Notes:
* - Cannot be called from the websocket event handler
*
* @param[in] client The client
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client);
/**
* @brief Write binary data to the WebSocket connection (data send with WS OPCODE=02, i.e. binary)
*
* @param[in] client The client
* @param[in] data The data
* @param[in] len The length
* @param[in] timeout Write data timeout in RTOS ticks
*
* @return
* - Number of data was sent
* - (-1) if any errors
*/
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
/**
* @brief Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text)
*
* @param[in] client The client
* @param[in] data The data
* @param[in] len The length
* @param[in] timeout Write data timeout in RTOS ticks
*
* @return
* - Number of data was sent
* - (-1) if any errors
*/
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
/**
* @brief Close the WebSocket connection in a clean way
*
* Sequence of clean close initiated by client:
* * Client sends CLOSE frame
* * Client waits until server echos the CLOSE frame
* * Client waits until server closes the connection
* * Client is stopped the same way as by the `esp_websocket_client_stop()`
*
* Notes:
* - Cannot be called from the websocket event handler
*
* @param[in] client The client
* @param[in] timeout Timeout in RTOS ticks for waiting
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_close(esp_websocket_client_handle_t client, TickType_t timeout);
/**
* @brief Close the WebSocket connection in a clean way with custom code/data
* Closing sequence is the same as for esp_websocket_client_close()
*
* Notes:
* - Cannot be called from the websocket event handler
*
* @param[in] client The client
* @param[in] code Close status code as defined in RFC6455 section-7.4
* @param[in] data Additional data to closing message
* @param[in] len The length of the additional data
* @param[in] timeout Timeout in RTOS ticks for waiting
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_close_with_code(esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout);
/**
* @brief Check the WebSocket client connection state
*
* @param[in] client The client handle
*
* @return
* - true
* - false
*/
bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client);
/**
* @brief Get the ping interval sec for client.
*
* @param[in] client The client
*
* @return The ping interval in sec
*/
size_t esp_websocket_client_get_ping_interval_sec(esp_websocket_client_handle_t client);
/**
* @brief Set new ping interval sec for client.
*
* @param[in] client The client
* @param[in] ping_interval_sec The new interval
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_set_ping_interval_sec(esp_websocket_client_handle_t client, size_t ping_interval_sec);
/**
* @brief Register the Websocket Events
*
* @param client The client handle
* @param event The event id
* @param event_handler The callback function
* @param event_handler_arg User context
* @return esp_err_t
*/
esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
esp_event_handler_t event_handler,
void *event_handler_arg);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
PRIV_REQUIRES cmock test_utils esp_websocket_client)

View File

@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* This test code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdlib.h>
#include <stdbool.h>
#include <esp_websocket_client.h>
#include "unity.h"
#include "memory_checks.h"
static void test_leak_setup(const char * file, long line)
{
printf("%s:%ld\n", file, line);
test_utils_record_free_mem();
}
TEST_CASE("websocket init and deinit", "[websocket][leaks=0]")
{
test_leak_setup(__FILE__, __LINE__);
const esp_websocket_client_config_t websocket_cfg = {
// no connection takes place, but the uri has to be valid for init() to succeed
.uri = "ws://echo.websocket.org",
};
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
TEST_ASSERT_NOT_EQUAL(NULL, client);
esp_websocket_client_destroy(client);
}
TEST_CASE("websocket init with invalid url", "[websocket][leaks=0]")
{
test_leak_setup(__FILE__, __LINE__);
const esp_websocket_client_config_t websocket_cfg = {
.uri = "INVALID",
};
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
TEST_ASSERT_NULL(client);
}
TEST_CASE("websocket set url with invalid url", "[websocket][leaks=0]")
{
test_leak_setup(__FILE__, __LINE__);
const esp_websocket_client_config_t websocket_cfg = {};
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
TEST_ASSERT_NOT_EQUAL(NULL, client);
TEST_ASSERT_NOT_EQUAL(ESP_OK, esp_websocket_client_set_uri(client, "INVALID"));
esp_websocket_client_destroy(client);
}

View File

@ -0,0 +1,22 @@
if(CONFIG_MDNS_NETWORKING_SOCKET)
set(MDNS_NETWORKING "mdns_networking_socket.c")
else()
set(MDNS_NETWORKING "mdns_networking_lwip.c")
endif()
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
set(dependencies esp_system_protocols_linux)
set(srcs "mdns.c" ${MDNS_NETWORKING})
else()
set(dependencies lwip console esp_netif)
set(private_dependencies esp_timer)
set(srcs "mdns.c" ${MDNS_NETWORKING} "mdns_console.c")
endif()
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include"
REQUIRES ${dependencies}
PRIV_REQUIRES ${private_dependencies})

127
components/mdns/Kconfig Normal file
View File

@ -0,0 +1,127 @@
menu "mDNS"
config MDNS_MAX_INTERFACES
int "Max number of interfaces"
range 1 9
default 3
help
Number of network interfaces to be served by the mDNS library.
Lowering this number helps to reduce some static RAM usage.
config MDNS_MAX_SERVICES
int "Max number of services"
range 1 64
default 10
help
Services take up a certain amount of memory, and allowing fewer
services to be open at the same time conserves memory. Specify
the maximum amount of services here. The valid value is from 1
to 64.
config MDNS_TASK_PRIORITY
int "mDNS task priority"
range 1 255
default 1
help
Allows setting mDNS task priority. Please do not set the task priority
higher than priorities of system tasks. Compile time warning/error
would be emitted if the chosen task priority were too high.
config MDNS_TASK_STACK_SIZE
int "mDNS task stack size"
default 4096
help
Allows setting mDNS task stacksize.
choice MDNS_TASK_AFFINITY
prompt "mDNS task affinity"
default MDNS_TASK_AFFINITY_CPU0
help
Allows setting mDNS tasks affinity, i.e. whether the task is pinned to
CPU0, pinned to CPU1, or allowed to run on any CPU.
config MDNS_TASK_AFFINITY_NO_AFFINITY
bool "No affinity"
config MDNS_TASK_AFFINITY_CPU0
bool "CPU0"
config MDNS_TASK_AFFINITY_CPU1
bool "CPU1"
depends on !FREERTOS_UNICORE
endchoice
config MDNS_TASK_AFFINITY
hex
default FREERTOS_NO_AFFINITY if MDNS_TASK_AFFINITY_NO_AFFINITY
default 0x0 if MDNS_TASK_AFFINITY_CPU0
default 0x1 if MDNS_TASK_AFFINITY_CPU1
config MDNS_SERVICE_ADD_TIMEOUT_MS
int "mDNS adding service timeout (ms)"
range 10 30000
default 2000
help
Configures timeout for adding a new mDNS service. Adding a service
fails if could not be completed within this time.
config MDNS_STRICT_MODE
bool "mDNS strict mode"
default "n"
help
Configures strict mode. Set this to 1 for the mDNS library to strictly follow the RFC6762:
Currently the only strict feature: Do not repeat original questions in response packets
(defined in RFC6762 sec. 6).
Default configuration is 0, i.e. non-strict mode, since some implementations,
such as lwIP mDNS resolver (used by standard POSIX API like getaddrinfo, gethostbyname)
could not correctly resolve advertised names.
config MDNS_TIMER_PERIOD_MS
int "mDNS timer period (ms)"
range 10 10000
default 100
help
Configures period of mDNS timer, which periodically transmits packets
and schedules mDNS searches.
config MDNS_NETWORKING_SOCKET
bool "Use BSD sockets for mDNS networking"
default n
help
Enables optional mDNS networking implementation using BSD sockets
in UDP multicast mode.
This option creates a new thread to serve receiving packets (TODO).
This option uses additional N sockets, where N is number of interfaces.
config MDNS_MULTIPLE_INSTANCE
bool "Multiple instances under the same service type"
default y
help
Enables adding multiple service instances under the same service type.
menu "MDNS Predefined interfaces"
config MDNS_PREDEF_NETIF_STA
bool "Use predefined interface for WiFi Station"
default y
help
Set up mDNS for the default WiFi station.
Disable this option if you do not need mDNS on default WiFi STA.
config MDNS_PREDEF_NETIF_AP
bool "Use predefined interface for WiFi Access Point"
default y
help
Set up mDNS for the default WiFi Access Point.
Disable this option if you do not need mDNS on default WiFi AP.
config MDNS_PREDEF_NETIF_ETH
bool "Use predefined interface for Ethernet"
depends on ETH_ENABLED
default y
help
Set up mDNS for the default Ethernet interface.
Disable this option if you do not need mDNS on default Ethernet.
endmenu # MDNS Predefined interfaces
endmenu

12
components/mdns/README.md Normal file
View File

@ -0,0 +1,12 @@
# mDNS Service
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
## Examples
Get started with example test [Example](examples/README.md):
## Documentation
* View the full [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* View the full [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)

75
components/mdns/docs/Doxyfile Executable file
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/mdns.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.

View File

@ -0,0 +1,21 @@
from esp_docs.conf_docs import * # noqa: F403,F401
extensions += ['sphinx_copybutton',
# Needed as a trigger for running doxygen
'esp_docs.esp_extensions.dummy_build_system',
'esp_docs.esp_extensions.run_doxygen',
]
# link roles config
github_repo = 'espressif/esp-protocols'
# context used by sphinx_idf_theme
html_context['github_user'] = 'espressif'
html_context['github_repo'] = 'esp-protocols'
# Extra options required by sphinx_idf_theme
project_slug = 'esp-idf' # >=5.0
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
idf_targets = ['esp32']
languages = ['en', 'zh_CN']

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses ../conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
try:
from conf_common import * # noqa: F403,F401
except ImportError:
import os
import sys
sys.path.insert(0, os.path.abspath('../'))
from conf_common import * # noqa: F403,F401
# General information about the project.
project = u'ESP-Protocols'
copyright = u'2016 - 2022, Espressif Systems (Shanghai) Co., Ltd'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'

View File

@ -0,0 +1,205 @@
mDNS Service
============
:link_to_translation:`zh_CN:[中文]`
Overview
--------
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
mDNS is installed by default on most operating systems or is available as separate package. On ``Mac OS`` it is installed by default and is called ``Bonjour``. Apple releases an installer for ``Windows`` that can be found `on Apple's support page <https://support.apple.com/downloads/bonjour%2520for%2520windows>`_. On ``Linux``, mDNS is provided by `avahi <https://github.com/lathiat/avahi>`_ and is usually installed by default.
mDNS Properties
^^^^^^^^^^^^^^^
* ``hostname``: the hostname that the device will respond to. If not set, the ``hostname`` will be read from the interface. Example: ``my-{IDF_TARGET_PATH_NAME}`` will resolve to ``my-{IDF_TARGET_PATH_NAME}.local``
* ``default_instance``: friendly name for your device, like ``Jhon's {IDF_TARGET_NAME} Thing``. If not set, ``hostname`` will be used.
Example method to start mDNS for the STA interface and set ``hostname`` and ``default_instance``:
.. highlight:: c
::
void start_mdns_service()
{
//initialize mDNS service
esp_err_t err = mdns_init();
if (err) {
printf("MDNS Init failed: %d\n", err);
return;
}
//set hostname
mdns_hostname_set("my-{IDF_TARGET_PATH_NAME}");
//set default instance
mdns_instance_name_set("Jhon's {IDF_TARGET_NAME} Thing");
}
mDNS Services
^^^^^^^^^^^^^
mDNS can advertise information about network services that your device offers. Each service is defined by a few properties.
* ``instance_name``: friendly name for your service, like ``Jhon's E{IDF_TARGET_NAME} Web Server``. If not defined, ``default_instance`` will be used.
* ``service_type``: (required) service type, prepended with underscore. Some common types can be found `here <http://www.dns-sd.org/serviceTypes.html>`_.
* ``proto``: (required) protocol that the service runs on, prepended with underscore. Example: ``_tcp`` or ``_udp``
* ``port``: (required) network port that the service runs on
* ``txt``: ``{var, val}`` array of strings, used to define properties for your service
Example method to add a few services and different properties::
void add_mdns_services()
{
//add our services
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
mdns_service_add(NULL, "_arduino", "_tcp", 3232, NULL, 0);
mdns_service_add(NULL, "_myservice", "_udp", 1234, NULL, 0);
//NOTE: services must be added before their properties can be set
//use custom instance for the web server
mdns_service_instance_name_set("_http", "_tcp", "Jhon's {IDF_TARGET_NAME} Web Server");
mdns_txt_item_t serviceTxtData[3] = {
{"board","{{IDF_TARGET_PATH_NAME}}"},
{"u","user"},
{"p","password"}
};
//set txt data for service (will free and replace current data)
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
//change service port
mdns_service_port_set("_myservice", "_udp", 4321);
}
mDNS Query
^^^^^^^^^^
mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses.
Results for services are returned as a linked list of ``mdns_result_t`` objects.
Example method to resolve host IPs::
void resolve_mdns_host(const char * host_name)
{
printf("Query A: %s.local", host_name);
struct ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if(err){
if(err == ESP_ERR_NOT_FOUND){
printf("Host was not found!");
return;
}
printf("Query Failed");
return;
}
printf(IPSTR, IP2STR(&addr));
}
Example method to resolve local services::
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
void mdns_print_results(mdns_result_t * results){
mdns_result_t * r = results;
mdns_ip_addr_t * a = NULL;
int i = 1, t;
while(r){
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
if(r->instance_name){
printf(" PTR : %s\n", r->instance_name);
}
if(r->hostname){
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
}
if(r->txt_count){
printf(" TXT : [%u] ", r->txt_count);
for(t=0; t<r->txt_count; t++){
printf("%s=%s; ", r->txt[t].key, r->txt[t].value);
}
printf("\n");
}
a = r->addr;
while(a){
if(a->addr.type == IPADDR_TYPE_V6){
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
r = r->next;
}
}
void find_mdns_service(const char * service_name, const char * proto)
{
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
mdns_result_t * results = NULL;
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
if(err){
ESP_LOGE(TAG, "Query Failed");
return;
}
if(!results){
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
Example of using the methods above::
void my_app_some_method(){
//search for {IDF_TARGET_PATH_NAME}-mdns.local
resolve_mdns_host("{IDF_TARGET_PATH_NAME}-mdns");
//search for HTTP servers
find_mdns_service("_http", "_tcp");
//or file servers
find_mdns_service("_smb", "_tcp"); //windows sharing
find_mdns_service("_afpovertcp", "_tcp"); //apple sharing
find_mdns_service("_nfs", "_tcp"); //NFS server
find_mdns_service("_ftp", "_tcp"); //FTP server
//or networked printer
find_mdns_service("_printer", "_tcp");
find_mdns_service("_ipp", "_tcp");
}
Performance Optimization
^^^^^^^^^^^^^^^^^^^^^^^^
Execution Speed
^^^^^^^^^^^^^^^
- mDNS creates a task with default low priority 1 ``CONFIG_MDNS_TASK_PRIORITY`` (If ``CONFIG_FREERTOS_UNICORE`` enabeled it pinned to CPU0 (``CONFIG_MDNS_TASK_AFFINITY``).
Please check `Maximizing Execution Speed <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/performance/speed.html>`_ for more details.
Minimizing RAM Usage
^^^^^^^^^^^^^^^^^^^^
- mDNS creates a tasks with stack sizes configured by ``CONFIG_MDNS_TASK_STACK_SIZE``.
Please check `Minimizing RAM Usage <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/performance/ram-usage.html>`_ for more details.
Application Example
-------------------
mDNS server/scanner example: :example:`<../examples>`.
API Reference
-------------
.. include-build-file:: inc/mdns.inc

View File

@ -0,0 +1,28 @@
build-docs --target esp32 --language en
build-docs --target esp32 --language zh_CN
cp -rf _build/en/esp32/html html_en
cp -rf _build/zh_CN/esp32/html html_zh_CN
rm -rf _build __pycache__ tee
# Modifes some version and target fields of index.html
echo "<script type="text/javascript">
window.onload =(function() {
var myAnchor = document.getElementById('version-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'latest';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>" | tee -a html_en/index.html html_zh_CN/index.html > /dev/null

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses ../conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
try:
from conf_common import * # noqa: F403,F401
except ImportError:
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
from conf_common import * # noqa: F403,F401
import datetime
current_year = datetime.datetime.now().year
# General information about the project.
project = u'ESP-IDF 编程指南'
copyright = u'2016 - {} 乐鑫信息科技(上海)股份有限公司'.format(current_year)
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'zh_CN'

View File

@ -0,0 +1,189 @@
mDNS 服务
=========
:link_to_translation:`en:[English]`
概述
----
mDNS 是一种组播 UDP 服务,用来提供本地网络服务和主机发现。
绝大多数的操作系统默认都会安装 mDNS 服务,或者提供单独的安装包。``Mac OS`` 默认会安装名为 ``Bonjour`` 的服务(该服务基于 mDNS此外 Apple 还发布了适用于 Windows 系统的安装程序,可以在 `官方支持 <https://support.apple.com/downloads/bonjour%2520for%2520windows>`_ 找到。在 ``Linux``mDNS 服务由 `avahi <https://github.com/lathiat/avahi>`_ 提供,通常也会被默认安装。
mDNS 属性
^^^^^^^^^
* ``hostname``:设备会去响应的主机名,如果没有设置,会根据设备的网络接口名定义 ``hostname`` 。例如,``my-{IDF_TARGET_PATH_NAME}`` 会被解析为 ``my-{IDF_TARGET_PATH_NAME}.local``
* ``default_instance``:默认实例名(即易记的设备名),例如 ``Jhon's {IDF_TARGET_NAME} Thing``。如果没有设置,将会使用 ``hostname``
以下为 STA 接口启动 mDNS 服务并设置 ``hostname````default_instance`` 的示例方法:
.. highlight:: c
::
void start_mdns_service()
{
// 初始化 mDNS 服务
esp_err_t err = mdns_init();
if (err) {
printf("MDNS Init failed: %d\n", err);
return;
}
// 设置 hostname
mdns_hostname_set("my-{IDF_TARGET_PATH_NAME}");
// 设置默认实例
mdns_instance_name_set("Jhon's {IDF_TARGET_NAME} Thing");
}
mDNS 服务
^^^^^^^^^
mDNS 可以广播设备能够提供的网络服务的相关信息,每个服务会由以下属性构成。
* ``instance_name``:实例名(即易记的服务名),例如 ``Jhon's {IDF_TARGET_NAME} Web Server``。如果没有定义,会使用 ``default_instance``
* ``service_type``:(必需)服务类型,以下划线为前缀,`这里 <http://www.dns-sd.org/serviceTypes.html>`_ 列出了常见的类型。
* ``proto``:(必需)服务运行所依赖的协议,以下划线为前缀,例如 ``_tcp`` 或者 ``_udp``
* ``port``:(必需)服务运行所用的端口号。
* ``txt``:形如 ``{var, val}`` 的字符串数组,用于定义服务的属性。
添加一些服务和不同属性的示例方法::
void add_mdns_services()
{
// 添加服务
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
mdns_service_add(NULL, "_arduino", "_tcp", 3232, NULL, 0);
mdns_service_add(NULL, "_myservice", "_udp", 1234, NULL, 0);
// 注意:必须先添加服务,然后才能设置其属性
// web 服务器使用自定义的实例名
mdns_service_instance_name_set("_http", "_tcp", "Jhon's {IDF_TARGET_NAME} Web Server");
mdns_txt_item_t serviceTxtData[3] = {
{"board","{IDF_TARGET_PATH_NAME}"},
{"u","user"},
{"p","password"}
};
// 设置服务的文本数据(会释放并替换当前数据)
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
// 修改服务端口号
mdns_service_port_set("_myservice", "_udp", 4321);
}
mDNS 查询
^^^^^^^^^
mDNS 提供查询服务和解析主机 IP/IPv6 地址的方法。
服务查询的结果会作为 ``mdns_result_t`` 类型对象的链表返回。
解析主机 IP 地址的示例方法::
void resolve_mdns_host(const char * host_name)
{
printf("Query A: %s.local", host_name);
struct ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if(err){
if(err == ESP_ERR_NOT_FOUND){
printf("Host was not found!");
return;
}
printf("Query Failed");
return;
}
printf(IPSTR, IP2STR(&addr));
}
解析本地服务的示例方法::
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
void mdns_print_results(mdns_result_t * results){
mdns_result_t * r = results;
mdns_ip_addr_t * a = NULL;
int i = 1, t;
while(r){
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
if(r->instance_name){
printf(" PTR : %s\n", r->instance_name);
}
if(r->hostname){
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
}
if(r->txt_count){
printf(" TXT : [%u] ", r->txt_count);
for(t=0; t<r->txt_count; t++){
printf("%s=%s; ", r->txt[t].key, r->txt[t].value);
}
printf("\n");
}
a = r->addr;
while(a){
if(a->addr.type == IPADDR_TYPE_V6){
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
r = r->next;
}
}
void find_mdns_service(const char * service_name, const char * proto)
{
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
mdns_result_t * results = NULL;
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
if(err){
ESP_LOGE(TAG, "Query Failed");
return;
}
if(!results){
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
使用上述方法的示例::
void my_app_some_method(){
// 搜索 {IDF_TARGET_PATH_NAME}-mdns.local
resolve_mdns_host("{IDF_TARGET_PATH_NAME}-mdns");
// 搜索 HTTP 服务器
find_mdns_service("_http", "_tcp");
// 或者搜索文件服务器
find_mdns_service("_smb", "_tcp"); // Windows 系统的共享服务
find_mdns_service("_afpovertcp", "_tcp"); // Apple AFP 文件共享服务
find_mdns_service("_nfs", "_tcp"); // NFS 服务器
find_mdns_service("_ftp", "_tcp"); // FTP 服务器
// 或者网络打印机
find_mdns_service("_printer", "_tcp");
find_mdns_service("_ipp", "_tcp");
}
应用示例
--------
有关 mDNS 服务器和查询器的应用示例请参考 :example:`<../examples>`
API 参考
--------
.. include-build-file:: inc/mdns.inc

View File

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mdns_test)

View File

@ -0,0 +1,91 @@
# mDNS example
Shows how to use mDNS to advertise lookup services and hosts
## Example workflow
- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host.
- WiFi STA is started and trying to connect to the access point defined through the project configuration
- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down
- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press
- Example task is started to check if the button is pressed so it can execute the mDNS queries defined
### Configure the project
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu
* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name`
* Disable `Resolve test services` to prevent the example from querying defined names/services on startup (cause warnings in example logs, as illustrated below)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
- Wait for WiFi to connect to your access point
- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is preconfigured hostname, `esp32-mdns` by default.
- You can also browse for `_http._tcp` on the same network to find the advertised service
- Pressing the BOOT button will start querying the local network for the predefined in `check_button` hosts and services
- Note that for purpose of CI tests, configuration options of `MDNS_RESOLVE_TEST_SERVICES` and `MDNS_ADD_MAC_TO_HOSTNAME` are available, but disabled by default. If enabled, then the hostname suffix of last 3 bytes from device MAC address is added, e.g. `esp32-mdns-80FFFF`, and a query for test service is issued.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (0) cpu_start: Starting scheduler on APP CPU.
I (276) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (276) mdns-test: mdns hostname set to: [esp32-mdns]
I (286) wifi: wifi driver task: 3ffc2fa4, prio:23, stack:3584, core=0
I (286) wifi: wifi firmware version: a3be639
I (286) wifi: config NVS flash: enabled
I (296) wifi: config nano formating: disabled
I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (336) wifi: Init dynamic tx buffer num: 32
I (336) wifi: Init data frame dynamic rx buffer num: 32
I (336) wifi: Init management frame dynamic rx buffer num: 32
I (346) wifi: Init static rx buffer size: 1600
I (346) wifi: Init static rx buffer num: 10
I (346) wifi: Init dynamic rx buffer num: 32
I (356) mdns-test: Setting WiFi configuration SSID myssid...
I (426) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (426) wifi: mode : sta (30:ae:a4:80:FF:FF)
I (426) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (1756) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (2736) wifi: state: init -> auth (b0)
I (2756) wifi: state: auth -> assoc (0)
I (2766) wifi: state: assoc -> run (10)
I (2786) wifi: connected with myssid, channel 11
I (2786) wifi: pm start, type: 1
I (4786) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
I (21126) mdns-test: Query A: esp32.local
W (23176) mdns-test: ESP_ERR_NOT_FOUND: Host was not found!
I (23176) mdns-test: Query PTR: _arduino._tcp.local
W (26276) mdns-test: No results found!
I (26276) mdns-test: Query PTR: _http._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:80
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : switch4e4919
SRV : switch4e4919.local:80
TXT : [1] path=/config/authentication_page.htm;
A : 192.168.0.118
I (29396) mdns-test: Query PTR: _printer._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:515
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : HP Color LaserJet MFP M277dw (7C2E10)
```

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "mdns_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,55 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-mdns"
help
mDNS Hostname for example to use
config MDNS_INSTANCE
string "mDNS Instance Name"
default "ESP32 with mDNS"
help
mDNS Instance Name for example to use
config MDNS_PUBLISH_DELEGATE_HOST
bool "Publish a delegated host"
help
Enable publishing a delegated host other than ESP32.
The example will also add a mock service for this host.
config MDNS_RESOLVE_TEST_SERVICES
bool "Resolve test services"
default n
help
Enable resolving test services on startup.
These services are advertized and evaluated in automated tests.
When executed locally, these will not be resolved and warnings appear in the log.
Please set to false to disable initial querying to avoid warnings.
config MDNS_ADD_MAC_TO_HOSTNAME
bool "Add mac suffix to hostname"
default n
help
If enabled, a portion of MAC address is added to the hostname, this is used
for evaluation of tests in CI
config MDNS_BUTTON_GPIO
int "Button GPIO to trigger querries"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 0
help
Set the GPIO number used as mDNS test button
config MDNS_ADD_CUSTOM_NETIF
bool "Add user netif to mdns service"
default n
help
If enabled, we try to add a custom netif to mdns service.
Note that for using with common connection example code, we have to disable
all predefined interfaces in mdns component setup (since we're adding one
of the default interfaces)
endmenu

View File

@ -0,0 +1,343 @@
/* MDNS-SD Query and advertise Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif_ip_addr.h"
#include "esp_mac.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "mdns.h"
#include "driver/gpio.h"
#include "netdb.h"
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
#define EXAMPLE_BUTTON_GPIO CONFIG_MDNS_BUTTON_GPIO
static const char * TAG = "mdns-test";
static char * generate_hostname(void);
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
static void query_mdns_host_with_gethostbyname(char * host);
static void query_mdns_host_with_getaddrinfo(char * host);
#endif
static void initialise_mdns(void)
{
char * hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) );
//structure with TXT records
mdns_txt_item_t serviceTxtData[3] = {
{"board", "esp32"},
{"u", "user"},
{"p", "password"}
};
//initialize service
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) );
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
#if CONFIG_MDNS_MULTIPLE_INSTANCE
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0) );
#endif
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
char *delegated_hostname;
if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) {
abort();
}
mdns_ip_addr_t addr4, addr6;
esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4);
addr4.addr.type = ESP_IPADDR_TYPE_V4;
esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6);
addr6.addr.type = ESP_IPADDR_TYPE_V6;
addr4.next = &addr6;
addr6.next = NULL;
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) );
free(delegated_hostname);
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
//add another TXT item
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
//change TXT item value
ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) );
free(hostname);
}
/* these strings match mdns_ip_protocol_t enumeration */
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
static void mdns_print_results(mdns_result_t *results)
{
mdns_result_t *r = results;
mdns_ip_addr_t *a = NULL;
int i = 1, t;
while (r) {
printf("%d: Interface: %s, Type: %s, TTL: %u\n", i++, esp_netif_get_ifkey(r->esp_netif), ip_protocol_str[r->ip_protocol],
r->ttl);
if (r->instance_name) {
printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto);
}
if (r->hostname) {
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
}
if (r->txt_count) {
printf(" TXT : [%zu] ", r->txt_count);
for (t = 0; t < r->txt_count; t++) {
printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]);
}
printf("\n");
}
a = r->addr;
while (a) {
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
r = r->next;
}
}
static void query_mdns_service(const char * service_name, const char * proto)
{
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
mdns_result_t * results = NULL;
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
if(err){
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
return;
}
if(!results){
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
static bool check_and_print_result(mdns_search_once_t *search)
{
// Check if any result is available
mdns_result_t * result = NULL;
if (!mdns_query_async_get_results(search, 0, &result, NULL)) {
return false;
}
if (!result) { // search timeout, but no result
return true;
}
// If yes, print the result
mdns_ip_addr_t * a = result->addr;
while (a) {
if(a->addr.type == ESP_IPADDR_TYPE_V6){
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
// and free the result
mdns_query_results_free(result);
return true;
}
static void query_mdns_hosts_async(const char * host_name)
{
ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name);
mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL);
mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL);
while (s_a || s_aaaa) {
if (s_a && check_and_print_result(s_a)) {
ESP_LOGI(TAG, "Query A %s.local finished", host_name);
mdns_query_async_delete(s_a);
s_a = NULL;
}
if (s_aaaa && check_and_print_result(s_aaaa)) {
ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name);
mdns_query_async_delete(s_aaaa);
s_aaaa = NULL;
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
static void query_mdns_host(const char * host_name)
{
ESP_LOGI(TAG, "Query A: %s.local", host_name);
struct esp_ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if(err){
if(err == ESP_ERR_NOT_FOUND){
ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err));
return;
}
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
return;
}
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
}
static void initialise_button(void)
{
gpio_config_t io_conf = {0};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = BIT64(EXAMPLE_BUTTON_GPIO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
gpio_config(&io_conf);
}
static void check_button(void)
{
static bool old_level = true;
bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO);
if (!new_level && old_level) {
query_mdns_hosts_async("esp32-mdns");
query_mdns_host("esp32");
query_mdns_service("_arduino", "_tcp");
query_mdns_service("_http", "_tcp");
query_mdns_service("_printer", "_tcp");
query_mdns_service("_ipp", "_tcp");
query_mdns_service("_afpovertcp", "_tcp");
query_mdns_service("_smb", "_tcp");
query_mdns_service("_ftp", "_tcp");
query_mdns_service("_nfs", "_tcp");
}
old_level = new_level;
}
static void mdns_example_task(void *pvParameters)
{
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/* Send initial queries that are started by CI tester */
query_mdns_host("tinytester");
query_mdns_host_with_gethostbyname("tinytester-lwip.local");
query_mdns_host_with_getaddrinfo("tinytester-lwip.local");
#endif
while (1) {
check_button();
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
initialise_mdns();
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
#if defined(CONFIG_MDNS_ADD_CUSTOM_NETIF) && !defined(CONFIG_MDNS_PREDEF_NETIF_STA) && !defined(CONFIG_MDNS_PREDEF_NETIF_ETH)
/* Demonstration of adding a custom netif to mdns service, but we're adding the default example one,
* so we must disable all predefined interfaces (PREDEF_NETIF_STA, AP and ETH) first
*/
ESP_ERROR_CHECK(mdns_register_netif(EXAMPLE_INTERFACE));
/* It is not enough to just register the interface, we have to enable is manually.
* This is typically performed in "GOT_IP" event handler, but we call it here directly
* since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple.
*/
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4));
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4));
#endif
initialise_button();
xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL);
}
/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it.
* @return host name string allocated from the heap
*/
static char* generate_hostname(void)
{
#ifndef CONFIG_MDNS_ADD_MAC_TO_HOSTNAME
return strdup(CONFIG_MDNS_HOSTNAME);
#else
uint8_t mac[6];
char *hostname;
esp_read_mac(mac, ESP_MAC_WIFI_STA);
if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) {
abort();
}
return hostname;
#endif
}
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/**
* @brief Executes gethostbyname and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_gethostbyname(char * host)
{
struct hostent *res = gethostbyname(host);
if (res) {
unsigned int i = 0;
while (res->h_addr_list[i] != NULL) {
ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host, inet_ntoa(*(struct in_addr *) (res->h_addr_list[i])));
i++;
}
}
}
/**
* @brief Executes getaddrinfo and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_getaddrinfo(char * host)
{
struct addrinfo hints;
struct addrinfo * res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (!getaddrinfo(host, NULL, &hints, &res)) {
while (res) {
ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host,
res->ai_family == AF_INET?
inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr):
inet_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr));
res = res->ai_next;
}
}
}
#endif

View File

@ -0,0 +1,166 @@
import os
import re
import select
import socket
import struct
import subprocess
import time
from threading import Event, Thread
import dpkt
import dpkt.dns
import ttfw_idf
from tiny_test_fw import DUT
from tiny_test_fw.Utility import console_log
def get_dns_query_for_esp(esp_host):
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
dns.qd[0].name = esp_host + u'.local'
console_log('Created query for esp host: {} '.format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns(tester_host):
dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
dns.rcode = dpkt.dns.DNS_RCODE_NOERR
arr = dpkt.dns.DNS.RR()
arr.cls = dpkt.dns.DNS_IN
arr.type = dpkt.dns.DNS_A
arr.name = tester_host
arr.ip = socket.inet_aton('127.0.0.1')
dns.an.append(arr)
console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns_lwip(tester_host, id):
dns = dpkt.dns.DNS(b'\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64'
b'\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c'
b'\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c')
dns.qd[0].name = tester_host
dns.an[0].name = tester_host
dns.an[0].ip = socket.inet_aton('127.0.0.1')
dns.an[0].rdata = socket.inet_aton('127.0.0.1')
dns.id = id
print('Created answer to mdns (lwip) query: {} '.format(dns.__repr__()))
return dns.pack()
def mdns_server(esp_host, events):
UDP_IP = '0.0.0.0'
UDP_PORT = 5353
MCAST_GRP = '224.0.0.251'
TESTER_NAME = u'tinytester.local'
TESTER_NAME_LWIP = u'tinytester-lwip.local'
QUERY_TIMEOUT = 0.2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.setblocking(False)
sock.bind((UDP_IP, UDP_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
last_query_timepoint = time.time()
while not events['stop'].is_set():
try:
current_time = time.time()
if current_time - last_query_timepoint > QUERY_TIMEOUT:
last_query_timepoint = current_time
if not events['esp_answered'].is_set():
sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP, UDP_PORT))
if not events['esp_delegated_answered'].is_set():
sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), (MCAST_GRP, UDP_PORT))
timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
read_socks, _, _ = select.select([sock], [], [], timeout)
if not read_socks:
continue
data, addr = sock.recvfrom(1024)
dns = dpkt.dns.DNS(data)
if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A:
if dns.qd[0].name == TESTER_NAME:
console_log('Received query: {} '.format(dns.__repr__()))
sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP, UDP_PORT))
elif dns.qd[0].name == TESTER_NAME_LWIP:
console_log('Received query: {} '.format(dns.__repr__()))
sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr)
if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
console_log('Received answer from {}'.format(dns.an[0].name))
if dns.an[0].name == esp_host + u'.local':
console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
events['esp_answered'].set()
if dns.an[0].name == esp_host + u'-delegated.local':
console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
events['esp_delegated_answered'].set()
except socket.timeout:
break
except dpkt.UnpackError:
continue
def test_examples_protocol_mdns(env, config):
"""
steps: |
1. obtain IP address + init mdns example
2. get the dut host name (and IP address)
3. check the mdns name is accessible
4. check DUT output if mdns advertized host is resolved
"""
dut1 = env.get_dut('mdns-test', 'examples/protocols/mdns', dut_class=ttfw_idf.ESP32DUT, app_config_name=config)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, 'mdns_test.bin')
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance('mdns-test_bin_size', '{}KB'.format(bin_size // 1024))
# 1. start mdns application
dut1.start_app()
# 2. get the dut host name (and IP address)
specific_host = dut1.expect(re.compile(r'mdns hostname set to: \[([^\]]+)\]'), timeout=30)[0]
mdns_server_events = {'stop': Event(), 'esp_answered': Event(), 'esp_delegated_answered': Event()}
mdns_responder = Thread(target=mdns_server, args=(str(specific_host), mdns_server_events))
try:
ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)[0]
console_log('Connected to AP with IP: {}'.format(ip_address))
except DUT.ExpectTimeout:
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
try:
# 3. check the mdns name is accessible
mdns_responder.start()
if not mdns_server_events['esp_answered'].wait(timeout=30):
raise ValueError('Test has failed: did not receive mdns answer within timeout')
if not mdns_server_events['esp_delegated_answered'].wait(timeout=30):
raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout')
# 4. check DUT output if mdns advertized host is resolved
dut1.expect(re.compile(r'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30)
dut1.expect(re.compile(r'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
dut1.expect(re.compile(r'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
# 5. check the DUT answers to `dig` command
dig_output = subprocess.check_output(['dig', '+short', '-p', '5353', '@224.0.0.251',
'{}.local'.format(specific_host)])
console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
if not ip_address.encode('utf-8') in dig_output:
raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig'
"Output should've contained DUT's IP address:{}".format(ip_address))
finally:
mdns_server_events['stop'].set()
mdns_responder.join()
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
def test_examples_protocol_mdns_default(env, _):
test_examples_protocol_mdns(env, 'eth_def')
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
def test_examples_protocol_mdns_socket(env, _):
test_examples_protocol_mdns(env, 'eth_socket')
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
def test_examples_protocol_mdns_custom_netif(env, _):
test_examples_protocol_mdns(env, 'eth_custom_netif')
if __name__ == '__main__':
test_examples_protocol_mdns_default()

View File

@ -0,0 +1,19 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_MDNS_PREDEF_NETIF_STA=n
CONFIG_MDNS_PREDEF_NETIF_AP=n
CONFIG_MDNS_PREDEF_NETIF_ETH=n
CONFIG_MDNS_ADD_CUSTOM_NETIF=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_MDNS_BUTTON_GPIO=32

View File

@ -0,0 +1,15 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_MDNS_BUTTON_GPIO=32

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