Compare commits

..

328 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
159 changed files with 16812 additions and 186 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

View File

@ -21,25 +21,36 @@ jobs:
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs==0.2.1
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_websocket_client/index.html">esp-websocket-client</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;components/esp_websocket_client"
directories: "components/esp_modem;components/esp_websocket_client;components/mdns"
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

View File

@ -1,4 +1,4 @@
name: Build Websockets
name: AFL fuzzer compilation test
on: [push, pull_request]
@ -16,11 +16,15 @@ jobs:
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/esp_websocket_client/examples/
idf.py build
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

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

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

@ -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.14"
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

@ -121,6 +121,26 @@ class SIM7600: public GenericModule {
public:
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;
};
/**

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

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

@ -59,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:
@ -271,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__ );
@ -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

@ -55,9 +55,29 @@ 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::set_network_bands(const std::string& mode, const int* bands, int size)
{
return dce_commands::set_network_bands_sim76xx(dte.get(), mode, bands, size);
}
command_result SIM7600::set_gnss_power_mode(int mode)
{
return dce_commands::set_gnss_power_mode_sim76xx(dte.get(), mode);
}
command_result SIM7600::power_down()
{
return dce_commands::power_down_sim7xxx(dte.get());
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()

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;
@ -67,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);
@ -78,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

@ -128,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);
@ -154,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

@ -15,7 +15,7 @@ html_context['github_repo'] = 'esp-docs'
# Extra options required by sphinx_idf_theme
project_slug = 'esp-idf' # >=5.0
versions_url = 'https://dl.espressif.com/dl/esp-idf/idf_versions.js'
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
idf_targets = ['esp32']
languages = ['en']

View File

@ -109,6 +109,7 @@ struct esp_websocket_client {
char *rx_buffer;
char *tx_buffer;
int buffer_size;
bool last_fin;
ws_transport_opcodes_t last_opcode;
int payload_len;
int payload_offset;
@ -133,6 +134,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
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;
@ -161,6 +163,20 @@ static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_hand
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;
@ -548,6 +564,7 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
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 ) {
@ -613,12 +630,13 @@ static void esp_websocket_client_task(void *pv)
client->run = false;
break;
}
if (esp_transport_connect(client->transport,
client->config->host,
client->config->port,
client->config->network_timeout_ms) < 0) {
ESP_LOGE(TAG, "Error transport connect");
esp_websocket_client_abort_connection(client);
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);
@ -899,6 +917,38 @@ bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client)
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,

View File

@ -4,7 +4,7 @@ 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 $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(websocket_example)

View File

@ -1,146 +0,0 @@
from __future__ import print_function, unicode_literals
import os
import random
import re
import socket
import string
from threading import Event, Thread
import ttfw_idf
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
from tiny_test_fw import Utility
def get_my_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
class TestEcho(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, TestEcho)
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_echo(dut):
dut.expect('WEBSOCKET_EVENT_CONNECTED')
for i in range(0, 5):
dut.expect(re.compile(r'Received=hello (\d)'), timeout=30)
print('All echos received')
def test_close(dut):
code = dut.expect(re.compile(r'WEBSOCKET: Received closed message with code=(\d*)'), timeout=60)[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:
# Filter out color encoding
match = dut.expect(re.compile(r'Received=([a-zA-Z0-9]*).*\n'), timeout=30)[0]
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)))
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
def test_examples_protocol_websocket(env, extra_data):
"""
steps:
1. obtain IP address
2. connect to uri specified in the config
3. send and receive data
"""
dut1 = env.get_dut('websocket', 'examples/protocols/websocket', dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, 'websocket_example.bin')
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance('websocket_bin_size', '{}KB'.format(bin_size // 1024))
try:
if 'CONFIG_WEBSOCKET_URI_FROM_STDIN' in dut1.app.get_sdkconfig():
uri_from_stdin = True
else:
uri = dut1.app.get_sdkconfig()['CONFIG_WEBSOCKET_URI'].strip('"')
uri_from_stdin = False
except Exception:
print('ENV_TEST_FAILURE: Cannot find uri settings in sdkconfig')
raise
# start test
dut1.start_app()
if uri_from_stdin:
server_port = 4455
with Websocket(server_port) as ws:
uri = 'ws://{}:{}'.format(get_my_ip(), server_port)
print('DUT connecting to {}'.format(uri))
dut1.expect('Please enter uri of websocket endpoint', timeout=30)
dut1.write(uri)
test_echo(dut1)
# Message length should exceed DUT's buffer size to test fragmentation, default is 1024 byte
test_recv_long_msg(dut1, ws, 2000, 3)
test_close(dut1)
else:
print('DUT connecting to {}'.format(uri))
test_echo(dut1)
if __name__ == '__main__':
test_examples_protocol_websocket()

View File

@ -103,7 +103,6 @@ static void websocket_app_start(void)
#else
websocket_cfg.uri = CONFIG_WEBSOCKET_URI;
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);

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

@ -1,5 +1,6 @@
CONFIG_WEBSOCKET_URI_FROM_STDIN=y
CONFIG_WEBSOCKET_URI_FROM_STRING=n
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

View File

@ -43,6 +43,7 @@ typedef enum {
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 */
@ -238,6 +239,25 @@ esp_err_t esp_websocket_client_close_with_code(esp_websocket_client_handle_t cli
*/
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
*

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

View File

@ -0,0 +1,16 @@
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_NETWORKING_SOCKET=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,5 @@
version: "1.0.2"
description: mDNS
dependencies:
idf:
version: ">=5.0"

View File

@ -0,0 +1,773 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_MDNS_H_
#define ESP_MDNS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <esp_netif.h>
#define MDNS_TYPE_A 0x0001
#define MDNS_TYPE_PTR 0x000C
#define MDNS_TYPE_TXT 0x0010
#define MDNS_TYPE_AAAA 0x001C
#define MDNS_TYPE_SRV 0x0021
#define MDNS_TYPE_OPT 0x0029
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_TYPE_ANY 0x00FF
/**
* @brief Asynchronous query handle
*/
typedef struct mdns_search_once_s mdns_search_once_t;
typedef enum {
MDNS_EVENT_ENABLE_IP4 = 1 << 1,
MDNS_EVENT_ENABLE_IP6 = 1 << 2,
MDNS_EVENT_ANNOUNCE_IP4 = 1 << 3,
MDNS_EVENT_ANNOUNCE_IP6 = 1 << 4,
MDNS_EVENT_DISABLE_IP4 = 1 << 5,
MDNS_EVENT_DISABLE_IP6 = 1 << 6,
} mdns_event_actions_t;
/**
* @brief mDNS enum to specify the ip_protocol type
*/
typedef enum {
MDNS_IP_PROTOCOL_V4,
MDNS_IP_PROTOCOL_V6,
MDNS_IP_PROTOCOL_MAX
} mdns_ip_protocol_t;
/**
* @brief mDNS basic text item structure
* Used in mdns_service_add()
*/
typedef struct {
const char * key; /*!< item key name */
const char * value; /*!< item value string */
} mdns_txt_item_t;
/**
* @brief mDNS query linked list IP item
*/
typedef struct mdns_ip_addr_s {
esp_ip_addr_t addr; /*!< IP address */
struct mdns_ip_addr_s * next; /*!< next IP, or NULL for the last IP in the list */
} mdns_ip_addr_t;
/**
* @brief mDNS query type to be explicitly set to either Unicast or Multicast
*/
typedef enum {
MDNS_QUERY_UNICAST,
MDNS_QUERY_MULTICAST,
} mdns_query_transmission_type_t;
/**
* @brief mDNS query result structure
*/
typedef struct mdns_result_s {
struct mdns_result_s * next; /*!< next result, or NULL for the last result in the list */
esp_netif_t* esp_netif; /*!< ptr to corresponding esp-netif */
uint32_t ttl; /*!< time to live */
mdns_ip_protocol_t ip_protocol; /*!< ip_protocol type of the interface (v4/v6) */
// PTR
char * instance_name; /*!< instance name */
char * service_type; /*!< service type */
char * proto; /*!< srevice protocol */
// SRV
char * hostname; /*!< hostname */
uint16_t port; /*!< service port */
// TXT
mdns_txt_item_t * txt; /*!< txt record */
uint8_t *txt_value_len; /*!< array of txt value len of each record */
size_t txt_count; /*!< number of txt items */
// A and AAAA
mdns_ip_addr_t * addr; /*!< linked list of IP addresses found */
} mdns_result_t;
typedef void (*mdns_query_notify_t)(mdns_search_once_t *search);
/**
* @brief Initialize mDNS on given interface
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE when failed to register event handler
* - ESP_ERR_NO_MEM on memory error
* - ESP_FAIL when failed to start mdns task
*/
esp_err_t mdns_init(void);
/**
* @brief Stop and free mDNS server
*
*/
void mdns_free(void);
/**
* @brief Set the hostname for mDNS server
* required if you want to advertise services
*
* @param hostname Hostname to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_hostname_set(const char * hostname);
/**
* @brief Adds a hostname and address to be delegated
* A/AAAA queries will be replied for the hostname and
* services can be added to this host.
*
* @param hostname Hostname to add
* @param address_list The IP address list of the host
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*
*/
esp_err_t mdns_delegate_hostname_add(const char * hostname, const mdns_ip_addr_t *address_list);
/**
* @brief Remove a delegated hostname
* All the services added to this host will also be removed.
*
* @param hostname Hostname to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*
*/
esp_err_t mdns_delegate_hostname_remove(const char * hostname);
/**
* @brief Query whether a hostname has been added
*
* @param hostname Hostname to query
*
* @return
* - true The hostname has been added.
* - false The hostname has not been added.
*
*/
bool mdns_hostname_exists(const char * hostname);
/**
* @brief Set the default instance name for mDNS server
*
* @param instance_name Instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_instance_name_set(const char * instance_name);
/**
* @brief Add service to mDNS server
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance_name instance name to set. If NULL,
* global instance name or hostname will be used.
* Note that MDNS_MULTIPLE_INSTANCE config option
* needs to be enabled for adding multiple instances
* with the same instance type.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
* @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add service
*/
esp_err_t mdns_service_add(const char * instance_name, const char * service_type, const char * proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items);
/**
* @brief Add service to mDNS server with a delegated hostname
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance_name instance name to set. If NULL,
* global instance name or hostname will be used
* Note that MDNS_MULTIPLE_INSTANCE config option
* needs to be enabled for adding multiple instances
* with the same instance type.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param port service port
* @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add service
*/
esp_err_t mdns_service_add_for_host(const char * instance_name, const char * service_type, const char * proto,
const char * hostname, uint16_t port, mdns_txt_item_t txt[], size_t num_items);
/**
* @brief Check whether a service has been added.
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, checks for the local hostname.
*
* @return
* - true Correspondding service has been added.
* - false Service not found.
*/
bool mdns_service_exists(const char * service_type, const char * proto, const char * hostname);
/**
* @brief Check whether a service has been added.
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, checks for the local hostname.
*
* @return
* - true Correspondding service has been added.
* - false Service not found.
*/
bool mdns_service_exists_with_instance(const char *instance, const char *service_type, const char *proto,
const char *hostname);
/**
* @brief Remove service from mDNS server
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_remove(const char * service_type, const char * proto);
/**
* @brief Remove service from mDNS server with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_remove_for_host(const char *instance, const char * service_type, const char * proto, const char *hostname);
/**
* @brief Set instance name for service
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param instance_name instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_name_set(const char * service_type, const char * proto, const char * instance_name);
/**
* @brief Set instance name for service with hostname
*
* @param instance_old original instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param instance_name instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_name_set_for_host(const char * instance_old, const char * service_type, const char * proto, const char * hostname,
const char * instance_name);
/**
* @brief Set service port
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set(const char * service_type, const char * proto, uint16_t port);
/**
* @brief Set service port with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param port service port
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set_for_host(const char * instance, const char * service_type, const char * proto, const char * hostname,
uint16_t port);
/**
* @brief Replace all TXT items for service
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param txt array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_set(const char * service_type, const char * proto, mdns_txt_item_t txt[], uint8_t num_items);
/**
* @brief Replace all TXT items for service with hostname
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param txt array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_set_for_host(const char * instance, const char * service_type, const char * proto, const char * hostname,
mdns_txt_item_t txt[], uint8_t num_items);
/**
* @brief Set/Add TXT item for service TXT record
*
* @note The value length will be automatically decided by strlen
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to add/update
* @param value the new value of the key
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set(const char * service_type, const char * proto, const char * key, const char * value);
/**
* @brief Set/Add TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to add/update
* @param value the new value of the key
* @param value_len the length of the value
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service_type, const char *proto,
const char *key, const char *value, uint8_t value_len);
/**
* @brief Set/Add TXT item for service TXT record with hostname
*
* @note The value length will be automatically decided by strlen
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to add/update
* @param value the new value of the key
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host(const char * instance, const char * service_type, const char * proto, const char * hostname,
const char * key, const char * value);
/**
* @brief Set/Add TXT item for service TXT record with hostname and txt value length
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to add/update
* @param value the new value of the key
* @param value_len the length of the value
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char * instance, const char *service_type, const char *proto,
const char *hostname, const char *key,
const char *value, uint8_t value_len);
/**
* @brief Remove TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_remove(const char * service_type, const char * proto, const char * key);
/**
* @brief Remove TXT item for service TXT record with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_remove_for_host(const char * instance, const char * service_type, const char * proto, const char * hostname,
const char * key);
/**
* @brief Add subtype for service.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param subtype The subtype to add.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
/**
* @brief Remove and free all services from mDNS server
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t mdns_service_remove_all(void);
/**
* @brief Deletes the finished query. Call this only after the search has ended!
*
* @param search pointer to search object
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE search has not finished
* - ESP_ERR_INVALID_ARG pointer to search object is NULL
*/
esp_err_t mdns_query_async_delete(mdns_search_once_t* search);
/**
* @brief Get results from search pointer. Results available as a pointer to the output parameter.
* Pointer to search object has to be deleted via `mdns_query_async_delete` once the query has finished.
* The results although have to be freed manually.
*
* @param search pointer to search object
* @param timeout time in milliseconds to wait for answers
* @param results pointer to the results of the query
* @param num_results pointer to the number of the actual result items (set to NULL to ignore this return value)
*
* @return
* True if search has finished before or at timeout
* False if search timeout is over
*/
bool mdns_query_async_get_results(mdns_search_once_t* search, uint32_t timeout, mdns_result_t ** results, uint8_t * num_results);
/**
* @brief Query mDNS for host or service asynchronousely.
* Search has to be tested for progress and deleted manually!
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param timeout time in milliseconds during which mDNS query is active
* @param max_results maximum results to be collected
* @param notifier Notification function to be called when the result is ready, can be NULL
*
* @return mdns_search_once_s pointer to new search object if query initiated successfully.
* NULL otherwise.
*/
mdns_search_once_t *mdns_query_async_new(const char *name, const char *service_type, const char *proto, uint16_t type,
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier);
/**
* @brief Generic mDNS query
* All following query methods are derived from this one
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param transmission_type either Unicast query, or Multicast query
* @param timeout time in milliseconds to wait for answers.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
* results must be freed using mdns_query_results_free below
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG timeout was not given
*/
esp_err_t mdns_query_generic(const char * name, const char * service_type, const char * proto, uint16_t type,
mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t ** results);
/**
* @brief Query mDNS for host or service
*
* Note that querying PTR types sends Multicast query, all other types send Unicast queries
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param timeout time in milliseconds to wait for answers.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
* results must be freed using mdns_query_results_free below
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG timeout was not given
*/
esp_err_t mdns_query(const char * name, const char * service_type, const char * proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t ** results);
/**
* @brief Free query results
*
* @param results linked list of results to be freed
*/
void mdns_query_results_free(mdns_result_t * results);
/**
* @brief Query mDNS for service
*
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_ptr(const char * service_type, const char * proto, uint32_t timeout, size_t max_results, mdns_result_t ** results);
/**
* @brief Query mDNS for SRV record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_srv(const char * instance_name, const char * service_type, const char * proto, uint32_t timeout, mdns_result_t ** result);
/**
* @brief Query mDNS for TXT record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_txt(const char * instance_name, const char * service_type, const char * proto, uint32_t timeout, mdns_result_t ** result);
/**
* @brief Query mDNS for A record
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer.
* @param addr pointer to the resulting IP4 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_a(const char * host_name, uint32_t timeout, esp_ip4_addr_t * addr);
#if CONFIG_LWIP_IPV6
/**
* @brief Query mDNS for A record
*
* Please note that hostname must not contain domain name, as mDNS uses '.local' domain.
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer. If 0, max_results needs to be defined
* @param addr pointer to the resulting IP6 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_aaaa(const char * host_name, uint32_t timeout, esp_ip6_addr_t * addr);
#endif
/**
* @brief Register custom esp_netif with mDNS functionality
* mDNS service runs by default on preconfigured interfaces (STA, AP, ETH).
* This API enables running the service on any customized interface,
* either using standard WiFi or Ethernet driver or any kind of user defined driver.
*
* @param esp_netif Pointer to esp-netif interface
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running or this netif is already registered
* - ESP_ERR_NO_MEM not enough memory for this in interface in the netif list (see CONFIG_MDNS_MAX_INTERFACES)
*/
esp_err_t mdns_register_netif(esp_netif_t *esp_netif);
/**
* @brief Unregister esp-netif already registered in mDNS service
*
* @param esp_netif Pointer to esp-netif interface
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NOT_FOUND this esp-netif was not registered in mDNS service
*/
esp_err_t mdns_unregister_netif(esp_netif_t *esp_netif);
/**
* @brief Set esp_netif to a desired state, or perform a desired action, such as enable/disable this interface
* or send announcement packets to this netif
*
* * This function is used to enable (probe, resolve conflicts and announce), announce, or disable (send bye) mDNS
* services on the specified network interface.
* * This function must be called if users registers a specific interface using mdns_register_netif()
* to enable mDNS services on that interface.
* * This function could be used in IP/connection event handlers to automatically enable/announce mDNS services
* when network properties change and/or disable them on disconnection.
*
* @param esp_netif Pointer to esp-netif interface
* @param event_action Disable/Enable/Announce on this interface over IPv4/IPv6 protocol.
* Actions enumerated in mdns_event_actions_t type.
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running or this netif is not registered
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_netif_action(esp_netif_t *esp_netif, mdns_event_actions_t event_action);
#ifdef __cplusplus
}
#endif
#endif /* ESP_MDNS_H_ */

View File

@ -0,0 +1,22 @@
// Copyright 2015-2016 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.
#ifndef _MDNS_CONSOLE_H_
#define _MDNS_CONSOLE_H_
/**
* @brief Register MDNS functions with the console component
*/
void mdns_console_register(void);
#endif /* _MDNS_CONSOLE_H_ */

6279
components/mdns/mdns.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,370 @@
/*
* MDNS Server Networking
*
*/
#include <string.h>
#include "esp_log.h"
#include "lwip/ip_addr.h"
#include "lwip/pbuf.h"
#include "lwip/igmp.h"
#include "lwip/udp.h"
#include "lwip/mld6.h"
#include "lwip/priv/tcpip_priv.h"
#include "esp_system.h"
#include "esp_event.h"
#include "mdns_networking.h"
#include "esp_netif_net_stack.h"
extern mdns_server_t * _mdns_server;
/*
* MDNS Server Networking
*
*/
static const char *TAG = "MDNS_Networking";
static struct udp_pcb * _pcb_main = NULL;
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
/**
* @brief Low level UDP PCB Initialize
*/
static esp_err_t _udp_pcb_main_init(void)
{
if(_pcb_main) {
return ESP_OK;
}
_pcb_main = udp_new();
if (!_pcb_main) {
return ESP_ERR_NO_MEM;
}
if (udp_bind(_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) {
udp_remove(_pcb_main);
_pcb_main = NULL;
return ESP_ERR_INVALID_STATE;
}
_pcb_main->mcast_ttl = 255;
_pcb_main->remote_port = MDNS_SERVICE_PORT;
ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE));
udp_recv(_pcb_main, &_udp_recv, _mdns_server);
return ESP_OK;
}
/**
* @brief Low level UDP PCB Free
*/
static void _udp_pcb_main_deinit(void)
{
if(_pcb_main){
udp_recv(_pcb_main, NULL, NULL);
udp_disconnect(_pcb_main);
udp_remove(_pcb_main);
_pcb_main = NULL;
}
}
/**
* @brief Low level UDP Multicast membership control
*/
static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join)
{
struct netif * netif = NULL;
esp_netif_t *tcpip_if = _mdns_get_esp_netif(if_inx);
if (!esp_netif_is_netif_up(tcpip_if)) {
// Network interface went down before event propagated, skipping IGMP config
return ESP_ERR_INVALID_STATE;
}
netif = esp_netif_get_netif_impl(tcpip_if);
assert(netif);
if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
ip4_addr_t multicast_addr;
IP4_ADDR(&multicast_addr, 224, 0, 0, 251);
if(join){
if (igmp_joingroup_netif(netif, &multicast_addr)) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (igmp_leavegroup_netif(netif, &multicast_addr)) {
return ESP_ERR_INVALID_STATE;
}
}
}
#if CONFIG_LWIP_IPV6
else {
ip_addr_t multicast_addr = IPADDR6_INIT(0x000002ff, 0, 0, 0xfb000000);
if(join){
if (mld6_joingroup_netif(netif, &(multicast_addr.u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (mld6_leavegroup_netif(netif, &(multicast_addr.u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
}
}
#endif
return ESP_OK;
}
/**
* @brief the receive callback of the raw udp api. Packets are received here
*
*/
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport)
{
uint8_t i;
while (pb != NULL) {
struct pbuf * this_pb = pb;
pb = pb->next;
this_pb->next = NULL;
mdns_rx_packet_t * packet = (mdns_rx_packet_t *)malloc(sizeof(mdns_rx_packet_t));
if (!packet) {
HOOK_MALLOC_FAILED;
//missed packet - no memory
pbuf_free(this_pb);
continue;
}
packet->tcpip_if = MDNS_MAX_INTERFACES;
packet->pb = this_pb;
packet->src_port = rport;
#if CONFIG_LWIP_IPV6
packet->src.type = raddr->type;
memcpy(&packet->src.u_addr, &raddr->u_addr, sizeof(raddr->u_addr));
#else
packet->src.type = IPADDR_TYPE_V4;
memcpy(&packet->src.u_addr.ip4, &raddr->addr, sizeof(ip_addr_t));
#endif
packet->dest.type = packet->src.type;
if (packet->src.type == IPADDR_TYPE_V4) {
packet->ip_protocol = MDNS_IP_PROTOCOL_V4;
struct ip_hdr * iphdr = (struct ip_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP_HLEN);
packet->dest.u_addr.ip4.addr = iphdr->dest.addr;
packet->multicast = ip4_addr_ismulticast(&(packet->dest.u_addr.ip4));
}
#if CONFIG_LWIP_IPV6
else {
packet->ip_protocol = MDNS_IP_PROTOCOL_V6;
struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP6_HLEN);
memcpy(&packet->dest.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
packet->multicast = ip6_addr_ismulticast(&(packet->dest.u_addr.ip6));
}
#endif
//lwip does not return the proper pcb if you have more than one for the same multicast address (but different interfaces)
struct netif * netif = NULL;
struct udp_pcb * pcb = NULL;
for (i=0; i<MDNS_MAX_INTERFACES; i++) {
pcb = _mdns_server->interfaces[i].pcbs[packet->ip_protocol].pcb;
netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
if (pcb && netif && netif == ip_current_input_netif ()) {
if (packet->src.type == IPADDR_TYPE_V4) {
#if CONFIG_LWIP_IPV6
if ((packet->src.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr) != (netif->ip_addr.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr)) {
#else
if ((packet->src.u_addr.ip4.addr & netif->netmask.addr) != (netif->ip_addr.addr & netif->netmask.addr)) {
#endif //packet source is not in the same subnet
pcb = NULL;
break;
}
}
packet->tcpip_if = i;
break;
}
pcb = NULL;
}
if (!pcb || !_mdns_server || !_mdns_server->action_queue
|| _mdns_send_rx_action(packet) != ESP_OK) {
pbuf_free(this_pb);
free(packet);
}
}
}
/**
* @brief Check if any of the interfaces is up
*/
static bool _udp_pcb_is_in_use(void){
int i, p;
for (i=0; i<MDNS_MAX_INTERFACES; i++) {
for (p=0; p<MDNS_IP_PROTOCOL_MAX; p++) {
if(_mdns_server->interfaces[i].pcbs[p].pcb){
return true;
}
}
}
return false;
}
/**
* @brief Stop PCB Main code
*/
static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (!_mdns_server) {
return;
}
mdns_pcb_t * _pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
if (_pcb->pcb) {
free(_pcb->probe_services);
_pcb->state = PCB_OFF;
_pcb->pcb = NULL;
_pcb->probe_ip = false;
_pcb->probe_services = NULL;
_pcb->probe_services_len = 0;
_pcb->probe_running = false;
_pcb->failed_probes = 0;
_udp_join_group(tcpip_if, ip_protocol, false);
if(!_udp_pcb_is_in_use()) {
_udp_pcb_main_deinit();
}
}
}
/**
* @brief Start PCB Main code
*/
static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (!_mdns_server || _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t err = _udp_join_group(tcpip_if, ip_protocol, true);
if(err){
return err;
}
err = _udp_pcb_main_init();
if(err){
return err;
}
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = _pcb_main;
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
return ESP_OK;
}
typedef struct {
struct tcpip_api_call_data call;
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
struct pbuf *pbt;
const ip_addr_t *ip;
uint16_t port;
esp_err_t err;
} mdns_api_call_t;
/**
* @brief Start PCB from LwIP thread
*/
static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg)
{
mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol);
return msg->err;
}
/**
* @brief Stop PCB from LwIP thread
*/
static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg)
{
mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
_udp_pcb_deinit(msg->tcpip_if, msg->ip_protocol);
msg->err = ESP_OK;
return ESP_OK;
}
/*
* Non-static functions below are
* - _mdns prefixed
* - commented in mdns_networking.h header
*/
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol
};
tcpip_api_call(_mdns_pcb_init_api, &msg.call);
return msg.err;
}
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol
};
tcpip_api_call(_mdns_pcb_deinit_api, &msg.call);
return msg.err;
}
static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
{
void * nif = NULL;
mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
mdns_pcb_t * _pcb = &_mdns_server->interfaces[msg->tcpip_if].pcbs[msg->ip_protocol];
nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if));
if (!nif) {
pbuf_free(msg->pbt);
msg->err = ERR_IF;
return ERR_IF;
}
esp_err_t err = udp_sendto_if (_pcb->pcb, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
pbuf_free(msg->pbt);
msg->err = err;
return err;
}
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len)
{
struct pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
if (pbt == NULL) {
return 0;
}
memcpy((uint8_t *)pbt->payload, data, len);
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol,
.pbt = pbt,
.ip = (ip_addr_t *)ip,
.port = port
};
tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call);
if (msg.err) {
return 0;
}
return len;
}
void* _mdns_get_packet_data(mdns_rx_packet_t *packet)
{
return packet->pb->payload;
}
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
{
return packet->pb->len;
}
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
pbuf_free(packet->pb);
free(packet);
}

View File

@ -0,0 +1,495 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief MDNS Server Networking module implemented using BSD sockets
*/
#include <string.h>
#include "esp_event.h"
#include "mdns_networking.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/param.h>
#include "esp_log.h"
#if defined(CONFIG_IDF_TARGET_LINUX)
#include <sys/ioctl.h>
#include <net/if.h>
#endif
extern mdns_server_t * _mdns_server;
static const char *TAG = "MDNS_Networking";
static bool s_run_sock_recv_task = false;
static int create_socket(esp_netif_t *netif);
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol);
#if defined(CONFIG_IDF_TARGET_LINUX)
// Need to define packet buffer struct on linux
struct pbuf {
struct pbuf * next;
void * payload;
size_t tot_len;
size_t len;
};
#else
// Compatibility define to access sock-addr struct the same way for lwip and linux
#define s6_addr32 un.u32_addr
#endif // CONFIG_IDF_TARGET_LINUX
static void delete_socket(int sock)
{
close(sock);
}
static struct udp_pcb* sock_to_pcb(int sock)
{
if (sock < 0) {
return NULL;
}
// Note: sock=0 is a valid descriptor, so save it as +1 ("1" is a valid pointer)
intptr_t sock_plus_one = sock + 1;
return (struct udp_pcb*)sock_plus_one;
}
static int pcb_to_sock(struct udp_pcb* pcb)
{
if (pcb == NULL) {
return -1;
}
intptr_t sock_plus_one = (intptr_t)pcb;
return sock_plus_one - 1;
}
void* _mdns_get_packet_data(mdns_rx_packet_t *packet)
{
return packet->pb->payload;
}
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
{
return packet->pb->len;
}
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
free(packet->pb->payload);
free(packet->pb);
free(packet);
}
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
struct udp_pcb * pcb = _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = NULL;
if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb == NULL &&
_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb == NULL) {
// if the interface for both protocol uninitialized, close the interface socket
int sock = pcb_to_sock(pcb);
if (sock >= 0) {
delete_socket(sock);
}
}
for (int i=0; i<MDNS_MAX_INTERFACES; i++) {
for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
if (_mdns_server->interfaces[i].pcbs[j].pcb)
// If any of the interfaces/protocol initialized
return ESP_OK;
}
}
// no interface alive, stop the rx task
s_run_sock_recv_task = false;
vTaskDelay(pdMS_TO_TICKS(500));
return ESP_OK;
}
#if defined(CONFIG_IDF_TARGET_LINUX)
#ifdef CONFIG_LWIP_IPV6
static char* inet6_ntoa_r(struct in6_addr addr, char* ptr, size_t size)
{
inet_ntop(AF_INET6, &(addr.s6_addr32[0]), ptr, size);
return ptr;
}
#endif // CONFIG_LWIP_IPV6
static char* inet_ntoa_r(struct in_addr addr, char* ptr, size_t size)
{
char * res = inet_ntoa(addr);
if (res && strlen(res) < size) {
strcpy(ptr, res);
}
return res;
}
#endif // CONFIG_IDF_TARGET_LINUX
static inline char* get_string_address(struct sockaddr_storage *source_addr)
{
static char address_str[40]; // 40=(8*4+7+term) is the max size of ascii IPv6 addr "XXXX:XX...XX:XXXX"
char *res = NULL;
// Convert ip address to string
if (source_addr->ss_family == PF_INET) {
res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str));
}
#ifdef CONFIG_LWIP_IPV6
else if (source_addr->ss_family == PF_INET6) {
res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str));
}
#endif
if (!res) {
address_str[0] = '\0'; // Returns empty string if conversion didn't succeed
}
return address_str;
}
static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t port, const mdns_ip_protocol_t ip_protocol, struct sockaddr_storage *in_addr)
{
size_t ss_addr_len = 0;
memset(in_addr, 0, sizeof(struct sockaddr_storage));
if (ip_protocol == MDNS_IP_PROTOCOL_V4 && addr->type == ESP_IPADDR_TYPE_V4) {
in_addr->ss_family = PF_INET;
#if !defined(CONFIG_IDF_TARGET_LINUX)
in_addr->s2_len = sizeof(struct sockaddr_in);
#endif
ss_addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *) in_addr;
in_addr_ip4->sin_port = port;
in_addr_ip4->sin_addr.s_addr = addr->u_addr.ip4.addr;
}
#if CONFIG_LWIP_IPV6
else if (ip_protocol == MDNS_IP_PROTOCOL_V6 && addr->type == ESP_IPADDR_TYPE_V6) {
memset(in_addr, 0, sizeof(struct sockaddr_storage));
in_addr->ss_family = PF_INET6;
#if !defined(CONFIG_IDF_TARGET_LINUX)
in_addr->s2_len = sizeof(struct sockaddr_in6);
#endif
ss_addr_len = sizeof(struct sockaddr_in6);
struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
in_addr_ip6->sin6_port = port;
u32_addr[0] = addr->u_addr.ip6.addr[0];
u32_addr[1] = addr->u_addr.ip6.addr[1];
u32_addr[2] = addr->u_addr.ip6.addr[2];
u32_addr[3] = addr->u_addr.ip6.addr[3];
}
#endif // CONFIG_LWIP_IPV6
return ss_addr_len;
}
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len)
{
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb);
if (sock < 0) {
return 0;
}
struct sockaddr_storage in_addr;
size_t ss_size = espaddr_to_inet(ip, htons(port), ip_protocol, &in_addr);
if (!ss_size) {
ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols");
return 0;
}
ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size);
if (actual_len < 0) {
ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n errno=%d: %s", sock, errno, strerror(errno));
}
return actual_len;
}
static inline void inet_to_espaddr(const struct sockaddr_storage *in_addr, esp_ip_addr_t *addr, uint16_t *port)
{
if (in_addr->ss_family == PF_INET) {
struct sockaddr_in * in_addr_ip4 = (struct sockaddr_in *)in_addr;
memset(addr, 0, sizeof(esp_ip_addr_t));
*port = in_addr_ip4->sin_port;
addr->u_addr.ip4.addr = in_addr_ip4->sin_addr.s_addr;
addr->type = ESP_IPADDR_TYPE_V4;
}
#if CONFIG_LWIP_IPV6
else if (in_addr->ss_family == PF_INET6) {
struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
memset(addr, 0, sizeof(esp_ip_addr_t));
*port = in_addr_ip6->sin6_port;
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
if (u32_addr[0] == 0 && u32_addr[1] == 0 && u32_addr[2] == esp_netif_htonl(0x0000FFFFUL)) {
// Mapped IPv4 address, convert directly to IPv4
addr->type = ESP_IPADDR_TYPE_V4;
addr->u_addr.ip4.addr = u32_addr[3];
} else {
addr->type = ESP_IPADDR_TYPE_V6;
addr->u_addr.ip6.addr[0] = u32_addr[0];
addr->u_addr.ip6.addr[1] = u32_addr[1];
addr->u_addr.ip6.addr[2] = u32_addr[2];
addr->u_addr.ip6.addr[3] = u32_addr[3];
}
}
#endif // CONFIG_LWIP_IPV6
}
void sock_recv_task(void* arg)
{
while (s_run_sock_recv_task) {
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
};
fd_set rfds;
FD_ZERO(&rfds);
int max_sock = -1;
for (int i=0; i<MDNS_MAX_INTERFACES; i++) {
for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
int sock = pcb_to_sock(_mdns_server->interfaces[i].pcbs[j].pcb);
if (sock >= 0) {
FD_SET(sock, &rfds);
max_sock = MAX(max_sock, sock);
}
}
}
if (max_sock < 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "No sock!");
continue;
}
int s = select(max_sock + 1, &rfds, NULL, NULL, &tv);
if (s < 0) {
ESP_LOGE(TAG, "Select failed. errno=%d: %s", errno, strerror(errno));
break;
} else if (s > 0) {
for (int tcpip_if=0; tcpip_if<MDNS_MAX_INTERFACES; tcpip_if++) {
// Both protocols share once socket
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb);
if (sock < 0) {
sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb);
}
if (sock < 0) {
continue;
}
if (FD_ISSET(sock, &rfds)) {
static char recvbuf[MDNS_MAX_PACKET_SIZE];
uint16_t port = 0;
struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(struct sockaddr_storage);
esp_ip_addr_t addr = {0};
int len = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
(struct sockaddr *) &raddr, &socklen);
if (len < 0) {
ESP_LOGE(TAG, "multicast recvfrom failed. errno=%d: %s", errno, strerror(errno));
break;
}
ESP_LOGD(TAG, "[sock=%d]: Received from IP:%s", sock, get_string_address(&raddr));
ESP_LOG_BUFFER_HEXDUMP(TAG, recvbuf, len, ESP_LOG_VERBOSE);
inet_to_espaddr(&raddr, &addr, &port);
// Allocate the packet structure and pass it to the mdns main engine
mdns_rx_packet_t *packet = (mdns_rx_packet_t *) calloc(1, sizeof(mdns_rx_packet_t));
struct pbuf *packet_pbuf = calloc(1, sizeof(struct pbuf));
uint8_t *buf = malloc(len);
if (packet == NULL || packet_pbuf == NULL || buf == NULL ) {
free(buf);
free(packet_pbuf);
free(packet);
HOOK_MALLOC_FAILED;
ESP_LOGE(TAG, "Failed to allocate the mdns packet");
continue;
}
memcpy(buf, recvbuf, len);
packet_pbuf->next = NULL;
packet_pbuf->payload = buf;
packet_pbuf->tot_len = len;
packet_pbuf->len = len;
packet->tcpip_if = tcpip_if;
packet->pb = packet_pbuf;
packet->src_port = ntohs(port);
memcpy(&packet->src, &addr, sizeof(esp_ip_addr_t));
// TODO(IDF-3651): Add the correct dest addr -- for mdns to decide multicast/unicast
// Currently it's enough to assume the packet is multicast and mdns to check the source port of the packet
memset(&packet->dest, 0, sizeof(esp_ip_addr_t));
packet->multicast = 1;
packet->dest.type = packet->src.type;
packet->ip_protocol =
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
if (!_mdns_server || !_mdns_server->action_queue || _mdns_send_rx_action(packet) != ESP_OK) {
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
free(packet->pb->payload);
free(packet->pb);
free(packet);
}
}
}
}
}
vTaskDelete(NULL);
}
static void mdns_networking_init(void)
{
if (s_run_sock_recv_task == false) {
s_run_sock_recv_task = true;
xTaskCreate( sock_recv_task, "mdns recv task", 3*1024, NULL, 5, NULL );
}
}
static struct udp_pcb* create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
return _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
}
mdns_ip_protocol_t other_ip_proto = ip_protocol==MDNS_IP_PROTOCOL_V4?MDNS_IP_PROTOCOL_V6:MDNS_IP_PROTOCOL_V4;
esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
if (_mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb) {
struct udp_pcb* other_pcb = _mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb;
int err = join_mdns_multicast_group(pcb_to_sock(other_pcb), netif, ip_protocol);
if (err < 0) {
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
return NULL;
}
return other_pcb;
}
int sock = create_socket(netif);
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create the socket!");
return NULL;
}
int err = join_mdns_multicast_group(sock, netif, ip_protocol);
if (err < 0) {
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
}
return sock_to_pcb(sock);
}
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%d, ip_protocol=%d)", tcpip_if, ip_protocol);
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = create_pcb(tcpip_if, ip_protocol);
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
mdns_networking_init();
return ESP_OK;
}
static int create_socket(esp_netif_t *netif)
{
#if CONFIG_LWIP_IPV6
int sock = socket(PF_INET6, SOCK_DGRAM, 0);
#else
int sock = socket(PF_INET, SOCK_DGRAM, 0);
#endif
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create socket. errno=%d: %s", errno, strerror(errno));
return -1;
}
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
ESP_LOGE(TAG, "Failed setsockopt() to set SO_REUSEADDR. errno=%d: %s\n", errno, strerror(errno));
}
// Bind the socket to any address
#if CONFIG_LWIP_IPV6
struct sockaddr_in6 saddr = { INADDR_ANY };
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(5353);
bzero(&saddr.sin6_addr.s6_addr, sizeof(saddr.sin6_addr.s6_addr));
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
if (err < 0) {
ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno));
goto err;
}
#else
struct sockaddr_in saddr = { 0 };
saddr.sin_family = AF_INET;
saddr.sin_port = htons(5353);
bzero(&saddr.sin_addr.s_addr, sizeof(saddr.sin_addr.s_addr));
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (err < 0) {
ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno));
goto err;
}
#endif // CONFIG_LWIP_IPV6
struct ifreq ifr;
esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface. errno=%d: %s", esp_netif_get_desc(netif), errno, strerror(errno));
goto err;
}
return sock;
err:
close(sock);
return -1;
}
#if CONFIG_LWIP_IPV6
static int socket_add_ipv6_multicast_group(int sock, esp_netif_t *netif)
{
int ifindex = esp_netif_get_netif_impl_index(netif);
int err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. errno=%d: %s", errno, strerror(errno));
return err;
}
struct ipv6_mreq v6imreq = { 0 };
esp_ip_addr_t multi_addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000);
memcpy(&v6imreq.ipv6mr_multiaddr, &multi_addr.u_addr.ip6.addr, sizeof(v6imreq.ipv6mr_multiaddr));
v6imreq.ipv6mr_interface = ifindex;
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &v6imreq, sizeof(struct ipv6_mreq));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. errno=%d: %s", errno, strerror(errno));
return err;
}
return err;
}
#endif // CONFIG_LWIP_IPV6
static int socket_add_ipv4_multicast_group(int sock, esp_netif_t *netif)
{
struct ip_mreq imreq = { 0 };
int err = 0;
esp_netif_ip_info_t ip_info = { 0 };
if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) {
ESP_LOGE(TAG, "Failed to esp_netif_get_ip_info()");
goto err;
}
imreq.imr_interface.s_addr = ip_info.ip.addr;
esp_ip_addr_t multicast_addr = ESP_IP4ADDR_INIT(224, 0, 0, 251);
imreq.imr_multiaddr.s_addr = multicast_addr.u_addr.ip4.addr;
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq));
if (err < 0) {
ESP_LOGE(TAG, "[sock=%d] Failed to set IP_ADD_MEMBERSHIP. errno=%d: %s", sock, errno, strerror(errno));
goto err;
}
err:
return err;
}
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol)
{
if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
return socket_add_ipv4_multicast_group(sock, netif);
}
#if CONFIG_LWIP_IPV6
if (ip_protocol == MDNS_IP_PROTOCOL_V6) {
return socket_add_ipv6_multicast_group(sock, netif);
}
#endif // CONFIG_LWIP_IPV6
return -1;
}

View File

@ -0,0 +1,53 @@
#ifndef ESP_MDNS_NETWORKING_H_
#define ESP_MDNS_NETWORKING_H_
/*
* MDNS Server Networking -- private include
*
*/
#include "mdns.h"
#include "mdns_private.h"
/**
* @brief Queue RX packet action
*/
esp_err_t _mdns_send_rx_action(mdns_rx_packet_t * packet);
/**
* @brief Start PCB
*/
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief Stop PCB
*/
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief send packet over UDP
*
* @param server The server
* @param data byte array containing the packet data
* @param len length of the packet data
*
* @return length of sent packet or 0 on error
*/
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len);
/**
* @brief Gets data pointer to the mDNS packet
*/
void* _mdns_get_packet_data(mdns_rx_packet_t *packet);
/**
* @brief Gets data length of c
*/
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet);
/**
* @brief Free the mDNS packet
*/
void _mdns_packet_free(mdns_rx_packet_t *packet);
#endif /* ESP_MDNS_NETWORKING_H_ */

View File

@ -0,0 +1,493 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef MDNS_PRIVATE_H_
#define MDNS_PRIVATE_H_
#include "sdkconfig.h"
#include "mdns.h"
#include "esp_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
//#define MDNS_ENABLE_DEBUG
#ifdef MDNS_ENABLE_DEBUG
#define _mdns_dbg_printf(...) printf(__VA_ARGS__)
#endif
/** mDNS strict mode: Set this to 1 for the mDNS library to strictly follow the RFC6762:
* Strict features:
* - to do not set original questions in response packets per RFC6762, sec 6
*
* The actual 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.
*/
#ifndef CONFIG_MDNS_STRICT_MODE
#define MDNS_STRICT_MODE 0
#else
#define MDNS_STRICT_MODE 1
#endif
#if !MDNS_STRICT_MODE
/* mDNS responders sometimes repeat queries in responses
* but according to RFC6762, sec 6: Responses MUST NOT contain
* any item in question field */
#define MDNS_REPEAT_QUERY_IN_RESPONSE 1
#endif
/** Number of predefined interfaces */
#ifndef CONFIG_MDNS_PREDEF_NETIF_STA
#define CONFIG_MDNS_PREDEF_NETIF_STA 0
#endif
#ifndef CONFIG_MDNS_PREDEF_NETIF_AP
#define CONFIG_MDNS_PREDEF_NETIF_AP 0
#endif
#ifndef CONFIG_MDNS_PREDEF_NETIF_ETH
#define CONFIG_MDNS_PREDEF_NETIF_ETH 0
#endif
#define MDNS_MAX_PREDEF_INTERFACES (CONFIG_MDNS_PREDEF_NETIF_STA + CONFIG_MDNS_PREDEF_NETIF_AP + CONFIG_MDNS_PREDEF_NETIF_ETH)
/** Number of configured interfaces */
#if MDNS_MAX_PREDEF_INTERFACES > CONFIG_MDNS_MAX_INTERFACES
#warning Number of configured interfaces is less then number of predefined interfaces. Please update CONFIG_MDNS_MAX_INTERFACES.
#define MDNS_MAX_INTERFACES (MDNS_MAX_PREDEF_INTERFACES)
#else
#define MDNS_MAX_INTERFACES (CONFIG_MDNS_MAX_INTERFACES)
#endif
/** The maximum number of services */
#define MDNS_MAX_SERVICES CONFIG_MDNS_MAX_SERVICES
#define MDNS_ANSWER_PTR_TTL 4500
#define MDNS_ANSWER_TXT_TTL 4500
#define MDNS_ANSWER_SRV_TTL 120
#define MDNS_ANSWER_A_TTL 120
#define MDNS_ANSWER_AAAA_TTL 120
#define MDNS_FLAGS_AUTHORITATIVE 0x8400
#define MDNS_FLAGS_DISTRIBUTED 0x0200
#define MDNS_NAME_REF 0xC000
//custom type! only used by this implementation
//to help manage service discovery handling
#define MDNS_TYPE_SDPTR 0x0032
#define MDNS_CLASS_IN 0x0001
#define MDNS_CLASS_ANY 0x00FF
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001
#define MDNS_ANSWER_ALL 0x3F
#define MDNS_ANSWER_PTR 0x08
#define MDNS_ANSWER_TXT 0x04
#define MDNS_ANSWER_SRV 0x02
#define MDNS_ANSWER_A 0x01
#define MDNS_ANSWER_AAAA 0x10
#define MDNS_ANSWER_NSEC 0x20
#define MDNS_ANSWER_SDPTR 0x80
#define MDNS_ANSWER_AAAA_SIZE 16
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
#define MDNS_SERVICE_STACK_DEPTH CONFIG_MDNS_TASK_STACK_SIZE
#define MDNS_TASK_PRIORITY CONFIG_MDNS_TASK_PRIORITY
#if (MDNS_TASK_PRIORITY > ESP_TASK_PRIO_MAX)
#error "mDNS task priority is higher than ESP_TASK_PRIO_MAX"
#elif (MDNS_TASK_PRIORITY > ESP_TASKD_EVENT_PRIO)
#warning "mDNS task priority is higher than ESP_TASKD_EVENT_PRIO, mDNS library might not work correctly"
#endif
#define MDNS_TASK_AFFINITY CONFIG_MDNS_TASK_AFFINITY
#define MDNS_SERVICE_ADD_TIMEOUT_MS CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
#define MDNS_ACTION_QUEUE_LEN 16 // Maximum actions pending to the server
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet
#define MDNS_HEAD_LEN 12
#define MDNS_HEAD_ID_OFFSET 0
#define MDNS_HEAD_FLAGS_OFFSET 2
#define MDNS_HEAD_QUESTIONS_OFFSET 4
#define MDNS_HEAD_ANSWERS_OFFSET 6
#define MDNS_HEAD_SERVERS_OFFSET 8
#define MDNS_HEAD_ADDITIONAL_OFFSET 10
#define MDNS_TYPE_OFFSET 0
#define MDNS_CLASS_OFFSET 2
#define MDNS_TTL_OFFSET 4
#define MDNS_LEN_OFFSET 8
#define MDNS_DATA_OFFSET 10
#define MDNS_SRV_PRIORITY_OFFSET 0
#define MDNS_SRV_WEIGHT_OFFSET 2
#define MDNS_SRV_PORT_OFFSET 4
#define MDNS_SRV_FQDN_OFFSET 6
#define MDNS_TIMER_PERIOD_US (CONFIG_MDNS_TIMER_PERIOD_MS*1000)
#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY)
#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore)
#define queueToEnd(type, queue, item) \
if (!queue) { \
queue = item; \
} else { \
type * _q = queue; \
while (_q->next) { _q = _q->next; } \
_q->next = item; \
}
#define queueDetach(type, queue, item) \
if (queue) { \
if (queue == item) { \
queue = queue->next; \
} else { \
type * _q = queue; \
while (_q->next && _q->next != item) { \
_q = _q->next; \
} \
if (_q->next == item) { \
_q->next = item->next; \
item->next = NULL; \
} \
} \
}
#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; free(_q); }
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)
#define PCB_STATE_IS_RUNNING(s) (s->state == PCB_RUNNING)
#ifndef HOOK_MALLOC_FAILED
#define HOOK_MALLOC_FAILED ESP_LOGE(TAG, "Cannot allocate memory (line: %d, free heap: %d bytes)", __LINE__, esp_get_free_heap_size());
#endif
typedef size_t mdns_if_t;
typedef enum {
PCB_OFF, PCB_DUP, PCB_INIT,
PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3,
PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3,
PCB_RUNNING
} mdns_pcb_state_t;
typedef enum {
MDNS_ANSWER, MDNS_NS, MDNS_EXTRA
} mdns_parsed_record_type_t;
typedef enum {
ACTION_SYSTEM_EVENT,
ACTION_HOSTNAME_SET,
ACTION_INSTANCE_SET,
ACTION_SERVICE_ADD,
ACTION_SERVICE_DEL,
ACTION_SERVICE_INSTANCE_SET,
ACTION_SERVICE_PORT_SET,
ACTION_SERVICE_TXT_REPLACE,
ACTION_SERVICE_TXT_SET,
ACTION_SERVICE_TXT_DEL,
ACTION_SERVICE_SUBTYPE_ADD,
ACTION_SERVICES_CLEAR,
ACTION_SEARCH_ADD,
ACTION_SEARCH_SEND,
ACTION_SEARCH_END,
ACTION_TX_HANDLE,
ACTION_RX_HANDLE,
ACTION_TASK_STOP,
ACTION_DELEGATE_HOSTNAME_ADD,
ACTION_DELEGATE_HOSTNAME_REMOVE,
ACTION_MAX
} mdns_action_type_t;
typedef struct {
uint16_t id;
union {
struct {
uint16_t qr :1;
uint16_t opCode :4;
uint16_t aa :1;
uint16_t tc :1;
uint16_t rd :1;
uint16_t ra :1;
uint16_t z :1;
uint16_t ad :1;
uint16_t cd :1;
uint16_t rCode :4;//response/error code
};
uint16_t value;
} flags;
uint16_t questions; //QDCOUNT
uint16_t answers; //ANCOUNT
uint16_t servers; //NSCOUNT
uint16_t additional;//ARCOUNT
} mdns_header_t;
typedef struct {
char host[MDNS_NAME_BUF_LEN]; // hostname for A/AAAA records, instance name for SRV records
char service[MDNS_NAME_BUF_LEN];
char proto[MDNS_NAME_BUF_LEN];
char domain[MDNS_NAME_BUF_LEN];
uint8_t parts;
uint8_t sub;
bool invalid;
} mdns_name_t;
typedef struct mdns_parsed_question_s {
struct mdns_parsed_question_s * next;
uint16_t type;
bool sub;
bool unicast;
char * host;
char * service;
char * proto;
char * domain;
} mdns_parsed_question_t;
typedef struct mdns_parsed_record_s {
struct mdns_parsed_record_s * next;
mdns_parsed_record_type_t record_type;
uint16_t type;
uint16_t clas;
uint8_t flush;
uint32_t ttl;
char * host;
char * service;
char * proto;
char * domain;
uint16_t data_len;
uint8_t *data;
} mdns_parsed_record_t;
typedef struct {
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
esp_ip_addr_t src;
uint16_t src_port;
uint8_t multicast;
uint8_t authoritative;
uint8_t probe;
uint8_t discovery;
uint8_t distributed;
mdns_parsed_question_t * questions;
mdns_parsed_record_t * records;
uint16_t id;
} mdns_parsed_packet_t;
typedef struct {
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
struct pbuf *pb;
esp_ip_addr_t src;
esp_ip_addr_t dest;
uint16_t src_port;
uint8_t multicast;
} mdns_rx_packet_t;
typedef struct mdns_txt_linked_item_s {
const char * key; /*!< item key name */
char * value; /*!< item value string */
uint8_t value_len; /*!< item value length */
struct mdns_txt_linked_item_s * next; /*!< next result, or NULL for the last result in the list */
} mdns_txt_linked_item_t;
typedef struct mdns_subtype_s {
const char *subtype; /*!< subtype */
struct mdns_subtype_s * next; /*!< next result, or NULL for the last result in the list */
} mdns_subtype_t;
typedef struct {
const char * instance;
const char * service;
const char * proto;
const char * hostname;
uint16_t priority;
uint16_t weight;
uint16_t port;
mdns_txt_linked_item_t * txt;
mdns_subtype_t *subtype;
} mdns_service_t;
typedef struct mdns_srv_item_s {
struct mdns_srv_item_s * next;
mdns_service_t * service;
} mdns_srv_item_t;
typedef struct mdns_out_question_s {
struct mdns_out_question_s * next;
uint16_t type;
bool unicast;
const char * host;
const char * service;
const char * proto;
const char * domain;
bool own_dynamic_memory;
} mdns_out_question_t;
typedef struct mdns_host_item_t {
const char * hostname;
mdns_ip_addr_t *address_list;
struct mdns_host_item_t *next;
} mdns_host_item_t;
typedef struct mdns_out_answer_s {
struct mdns_out_answer_s * next;
uint16_t type;
uint8_t bye;
uint8_t flush;
mdns_service_t * service;
mdns_host_item_t* host;
const char * custom_instance;
const char * custom_service;
const char * custom_proto;
} mdns_out_answer_t;
typedef struct mdns_tx_packet_s {
struct mdns_tx_packet_s * next;
uint32_t send_at;
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
esp_ip_addr_t dst;
uint16_t port;
uint16_t flags;
uint8_t distributed;
mdns_out_question_t * questions;
mdns_out_answer_t * answers;
mdns_out_answer_t * servers;
mdns_out_answer_t * additional;
bool queued;
uint16_t id;
} mdns_tx_packet_t;
typedef struct {
mdns_pcb_state_t state;
struct udp_pcb * pcb;
mdns_srv_item_t ** probe_services;
uint8_t probe_services_len;
uint8_t probe_ip;
uint8_t probe_running;
uint16_t failed_probes;
} mdns_pcb_t;
typedef enum {
SEARCH_OFF,
SEARCH_INIT,
SEARCH_RUNNING,
SEARCH_MAX
} mdns_search_once_state_t;
typedef struct mdns_search_once_s {
struct mdns_search_once_s * next;
mdns_search_once_state_t state;
uint32_t started_at;
uint32_t sent_at;
uint32_t timeout;
mdns_query_notify_t notifier;
SemaphoreHandle_t done_semaphore;
uint16_t type;
bool unicast;
uint8_t max_results;
uint8_t num_results;
char * instance;
char * service;
char * proto;
mdns_result_t * result;
} mdns_search_once_t;
typedef struct mdns_server_s {
struct {
mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];
} interfaces[MDNS_MAX_INTERFACES];
const char * hostname;
const char * instance;
mdns_srv_item_t * services;
SemaphoreHandle_t lock;
QueueHandle_t action_queue;
mdns_tx_packet_t * tx_queue_head;
mdns_search_once_t * search_once;
esp_timer_handle_t timer_handle;
} mdns_server_t;
typedef struct {
mdns_action_type_t type;
union {
struct {
char * hostname;
TaskHandle_t calling_task;
} hostname_set;
char * instance;
struct {
mdns_if_t interface;
mdns_event_actions_t event_action;
} sys_event;
struct {
mdns_srv_item_t * service;
} srv_add;
struct {
mdns_srv_item_t * service;
} srv_del;
struct {
mdns_srv_item_t * service;
char * instance;
} srv_instance;
struct {
mdns_srv_item_t * service;
uint16_t port;
} srv_port;
struct {
mdns_srv_item_t * service;
mdns_txt_linked_item_t * txt;
} srv_txt_replace;
struct {
mdns_srv_item_t * service;
char * key;
char * value;
uint8_t value_len;
} srv_txt_set;
struct {
mdns_srv_item_t * service;
char * key;
} srv_txt_del;
struct {
mdns_srv_item_t * service;
char * subtype;
} srv_subtype_add;
struct {
mdns_search_once_t * search;
} search_add;
struct {
mdns_tx_packet_t * packet;
} tx_handle;
struct {
mdns_rx_packet_t * packet;
} rx_handle;
struct {
const char * hostname;
mdns_ip_addr_t *address_list;
} delegate_hostname;
} data;
} mdns_action_t;
/*
* @brief Convert mnds if to esp-netif handle
*
* @param tcpip_if mdns supported interface as internal enum
*
* @return
* - ptr to esp-netif on success
* - NULL if no available netif for current interface index
*/
esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if);
#endif /* MDNS_PRIVATE_H_ */

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(mdns_host)

View File

@ -0,0 +1,25 @@
# Setup dummy network interfaces
```
sudo ip link add eth2 type dummy
sudo ip addr add 192.168.1.200/24 dev eth2
sudo ip link set eth2 up
sudo ifconfig eth2 multicast
```
# Dig on a specified interface
```
dig +short -b 192.168.1.200 -p 5353 @224.0.0.251 myesp.local
```
# Run avahi to browse services
Avahi needs the netif to have the "multicast" flag set
```bash
david@david-comp:~/esp/idf (feature/mdns_networking_socket)$ avahi-browse -a -r -p
+;eth2;IPv6;myesp-service2;Web Site;local
+;eth2;IPv4;myesp-service2;Web Site;local
=;eth2;IPv6;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
=;eth2;IPv4;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
```

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS esp_event_mock.c
INCLUDE_DIRS include
REQUIRES esp_system_protocols_linux)

View File

@ -0,0 +1,29 @@
// 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 "esp_err.h"
#include "esp_event.h"
const char * WIFI_EVENT = "WIFI_EVENT";
const char * IP_EVENT = "IP_EVENT";
esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id, void* event_handler, void* event_handler_arg)
{
return ESP_OK;
}
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler)
{
return ESP_OK;
}

View File

@ -0,0 +1,32 @@
// 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.
#pragma once
#include "stdbool.h"
#include "esp_err.h"
#include "esp_event_base.h"
#include "bsd_strings.h"
#define ESP_EVENT_DECLARE_BASE(x)
#define ESP_EVENT_ANY_ID (-1)
typedef void * esp_event_base_t;
typedef void * system_event_t;
const char* WIFI_EVENT;
const char* IP_EVENT;
esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id, void* event_handler, void* event_handler_arg);
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler);

View File

@ -0,0 +1,21 @@
// 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.
#pragma once
typedef enum {
WIFI_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
WIFI_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
WIFI_EVENT_AP_START, /**< ESP32 soft-AP start */
WIFI_EVENT_AP_STOP, /**< ESP32 soft-AP stop */
} mdns_used_event_t;

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS esp_netif_linux.c
INCLUDE_DIRS include
REQUIRES esp_system_protocols_linux)

View File

@ -0,0 +1,9 @@
menu "LWIP-MOCK-CONFIG"
config LWIP_IPV6
bool "Enable IPv6"
default y
help
Enable/disable IPv6
endmenu

View File

@ -0,0 +1,163 @@
// 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<stdio.h>
#include <stdlib.h>
#include "esp_netif.h"
#include "esp_err.h"
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include "esp_netif_types.h"
#define MAX_NETIFS 4
static esp_netif_t* s_netif_list[MAX_NETIFS] = { 0 };
struct esp_netif_obj
{
const char *if_key;
const char *if_desc;
};
esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key)
{
for (int i=0; i<MAX_NETIFS; ++i) {
if (s_netif_list[i] && strcmp(s_netif_list[i]->if_key, if_key) == 0) {
return s_netif_list[i];
}
}
return NULL;
}
esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
struct ifaddrs *addrs, *tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
char addr[20];
struct sockaddr_in *pAddr = (struct sockaddr_in *) tmp->ifa_addr;
inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr) );
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
printf("AF_INET: %s: %s\n", tmp->ifa_name, addr);
memcpy(&ip_info->ip.addr, &pAddr->sin_addr, 4);
break;
}
}
tmp = tmp->ifa_next;
}
return ESP_OK;
}
esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_status_t *status)
{
return ESP_OK;
}
esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
struct ifaddrs *addrs, *tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp)
{
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
char addr[64];
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr) );
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
printf("AF_INET6: %s: %s\n", tmp->ifa_name, addr);
memcpy(if_ip6->addr, &pAddr->sin6_addr, 4*4);
break;
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return ESP_OK;
}
int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif)
{
if (esp_netif == NULL) {
return -1;
}
uint32_t interfaceIndex = if_nametoindex(esp_netif->if_desc);
return interfaceIndex;
}
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
strcpy(name, esp_netif->if_desc);
return ESP_OK;
}
const char *esp_netif_get_desc(esp_netif_t *esp_netif)
{
if (esp_netif == NULL) {
return NULL;
}
return esp_netif->if_desc;
}
esp_netif_t *esp_netif_new(const esp_netif_config_t *config)
{
if (esp_netif_get_handle_from_ifkey(config->base->if_key)) {
return NULL;
}
esp_netif_t* netif = calloc(1, sizeof(struct esp_netif_obj));
if (netif) {
netif->if_desc = config->base->if_desc;
netif->if_key = config->base->if_key;
}
for (int i=0; i<MAX_NETIFS; ++i) {
if (s_netif_list[i] == NULL) {
s_netif_list[i] = netif;
break;
}
}
return netif;
}
void esp_netif_destroy(esp_netif_t *esp_netif)
{
for (int i=0; i<MAX_NETIFS; ++i) {
if (s_netif_list[i] == esp_netif) {
s_netif_list[i] = NULL;
break;
}
}
free(esp_netif);
}

View File

@ -0,0 +1,15 @@
// 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.
#pragma once
#include "esp_event.h"

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS esp_log_impl.c strlcat.c
INCLUDE_DIRS include
REQUIRES esp_netif_linux esp_timer_linux freertos_linux esp_event_mock esp_netif log esp_common)

View File

@ -0,0 +1,35 @@
// 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 "esp_err.h"
#include "esp_log.h"
#include <stdlib.h>
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
ESP_LOGE("ESP_ERROR_CHECK", "Failed with esp_err_t: 0x%x", rc);
ESP_LOGE("ESP_ERROR_CHECK", "Expression: %s", expression);
ESP_LOGE("ESP_ERROR_CHECK", "Functions: %s %s(%d)", function, file, line);
abort();
}
void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level)
{
if ( LOG_LOCAL_LEVEL >= log_level ) {
ESP_LOG_LEVEL(log_level, tag, "Buffer:%p length:%d", buffer, buff_len);
for (int i=0; i<buff_len; ++i) {
printf("%02x ", ((uint8_t*)buffer)[i]);
}
printf("\n");
}
}

View File

@ -0,0 +1,16 @@
// 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.
#pragma once
size_t strlcat(char *dest, const char *src, size_t size);

View File

@ -0,0 +1,16 @@
// 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.
#pragma once
#include_next "endian.h"

View File

@ -0,0 +1,68 @@
/* $OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $ */
/*-
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "string.h"
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(dst, src, siz)
char *dst;
const char *src;
size_t siz;
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}

View File

@ -0,0 +1,9 @@
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
INCLUDE_DIRS include
REQUIRES esp_system_protocols_linux freertos_linux)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)

View File

@ -0,0 +1,47 @@
// 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 "esp_err.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void * create_tt(esp_timer_cb_t cb);
void destroy_tt(void* tt);
void set_tout(void* tt, uint32_t ms);
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
esp_timer_handle_t* out_handle)
{
*out_handle = (esp_timer_handle_t)create_tt(create_args->callback);
return ESP_OK;
}
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period)
{
set_tout(timer, period/1000);
return ESP_OK;
}
esp_err_t esp_timer_stop(esp_timer_handle_t timer)
{
return ESP_OK;
}
esp_err_t esp_timer_delete(esp_timer_handle_t timer)
{
destroy_tt(timer);
return ESP_OK;
}

View File

@ -0,0 +1,41 @@
// 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.
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef struct esp_timer* esp_timer_handle_t;
typedef void (*esp_timer_cb_t)(void* arg);
typedef enum {
ESP_TIMER_TASK,
} esp_timer_dispatch_t;
typedef struct {
esp_timer_cb_t callback; //!< Function to call when timer expires
void* arg; //!< Argument to pass to the callback
esp_timer_dispatch_t dispatch_method; //!< Call the callback from task or from ISR
const char* name; //!< Timer name, used in esp_timer_dump function
bool skip_unhandled_events; //!< Skip unhandled events for periodic timers
} esp_timer_create_args_t;
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
esp_timer_handle_t* out_handle);
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
esp_err_t esp_timer_delete(esp_timer_handle_t timer);

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