Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d3a93cb

Browse files
committedMar 16, 2025·
feat(lws): Add lws server example
1 parent 2e28774 commit d3a93cb

15 files changed

+862
-0
lines changed
 
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: "lws: build-tests-server"
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
types: [opened, synchronize, reopened, labeled]
9+
10+
jobs:
11+
build_lws_server:
12+
if: contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push'
13+
name: Libwebsockets server build
14+
strategy:
15+
matrix:
16+
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
17+
test: [ { app: example, path: "examples/server-echo" }]
18+
runs-on: ubuntu-22.04
19+
container: espressif/idf:${{ matrix.idf_ver }}
20+
env:
21+
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
22+
steps:
23+
- name: Checkout esp-protocols
24+
uses: actions/checkout@v4
25+
with:
26+
submodules: recursive
27+
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
28+
shell: bash
29+
run: |
30+
. ${IDF_PATH}/export.sh
31+
python -m pip install idf-build-apps
32+
python ./ci/build_apps.py ${TEST_DIR}
33+
cd ${TEST_DIR}
34+
for dir in `ls -d build_esp32_*`; do
35+
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
36+
zip -qur artifacts.zip $dir
37+
done
38+
- uses: actions/upload-artifact@v4
39+
with:
40+
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
41+
path: ${{ env.TEST_DIR }}/artifacts.zip
42+
if-no-files-found: error
43+
44+
run-target-lws-server:
45+
if: |
46+
github.repository == 'espressif/esp-protocols' &&
47+
( contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push' )
48+
name: Target server test
49+
needs: build_lws_server
50+
strategy:
51+
fail-fast: false
52+
matrix:
53+
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
54+
idf_target: ["esp32"]
55+
test: [ { app: example, path: "examples/server-echo" }]
56+
runs-on:
57+
- self-hosted
58+
- ESP32-ETHERNET-KIT
59+
env:
60+
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
61+
62+
steps:
63+
- uses: actions/checkout@v4
64+
- uses: actions/download-artifact@v4
65+
with:
66+
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
67+
path: ${{ env.TEST_DIR }}/ci/
68+
- name: Install Python packages
69+
env:
70+
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
71+
run: |
72+
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt websocket
73+
- name: Run Example Test on target
74+
working-directory: ${{ env.TEST_DIR }}
75+
run: |
76+
unzip ci/artifacts.zip -d ci
77+
for dir in `ls -d ci/build_*`; do
78+
rm -rf build sdkconfig.defaults
79+
mv $dir build
80+
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
81+
done
82+
- uses: actions/upload-artifact@v4
83+
if: always()
84+
with:
85+
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
86+
path: components/libwebsockets/${{ matrix.test.path }}/*.xml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# The following lines of boilerplate have to be in your project's CMakeLists
2+
# in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
set(requirements 1)
5+
6+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
7+
project(server_echo_example)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Websocket LWS server example
2+
3+
This example will shows how to set up and communicate over a websocket.
4+
5+
## How to Use Example
6+
7+
### Hardware Required
8+
9+
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet or a local server.
10+
11+
### Configure the project
12+
13+
* Open the project configuration menu (`idf.py menuconfig`)
14+
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu.
15+
16+
### Server Certificate Verification
17+
18+
19+
### Generating a self signed Certificates with OpenSSL
20+
21+
22+
### Build and Flash
23+
24+
Build the project and flash it to the board, then run monitor tool to view serial output:
25+
26+
```
27+
idf.py -p PORT flash monitor
28+
```
29+
30+
(To exit the serial monitor, type ``Ctrl-]``.)
31+
32+
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
33+
34+
## Example Output
35+
36+
37+
## Python Flask echo server
38+
39+
By default, the `ws://echo.websocket.events` endpoint is used. You can setup a Python websocket echo server locally and try the `ws://<your-ip>:5000` endpoint. To do this, install Flask-sock Python package
40+
41+
```
42+
pip install flask-sock
43+
```
44+
45+
and start a Flask websocket echo server locally by executing the following Python code:
46+
47+
```python
48+
from flask import Flask
49+
from flask_sock import Sock
50+
51+
app = Flask(__name__)
52+
sock = Sock(app)
53+
54+
55+
@sock.route('/')
56+
def echo(ws):
57+
while True:
58+
data = ws.receive()
59+
ws.send(data)
60+
61+
62+
if __name__ == '__main__':
63+
# To run your Flask + WebSocket server in production you can use Gunicorn:
64+
# gunicorn -b 0.0.0.0:5000 --workers 4 --threads 100 module:app
65+
app.run(host="0.0.0.0", debug=True)
66+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
set(SRC_FILES "lws-server-echo.c") # Initialize SRC_FILES as an empty list
2+
set(INCLUDE_DIRS ".") # Define include directories
3+
set(EMBED_FILES) # Initialize EMBED_FILES as an empty list
4+
5+
list(APPEND EMBED_FILES
6+
"certs/server_cert.pem"
7+
"certs/ca_cert.pem"
8+
"certs/server_key.pem")
9+
10+
idf_component_register(SRCS "${SRC_FILES}"
11+
INCLUDE_DIRS "${INCLUDE_DIRS}"
12+
EMBED_TXTFILES "${EMBED_FILES}")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
menu "Example Configuration"
2+
config WEBSOCKET_PORT
3+
int "Websocket endpoint PORT"
4+
default 80
5+
help
6+
Port of websocket endpoint this example connects to and sends echo
7+
config WS_OVER_TLS
8+
bool "Enable WebSocket over TLS with Server Certificate"
9+
default y
10+
help
11+
Enables WebSocket connections over TLS (WSS)
12+
endmenu
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDazCCAlOgAwIBAgIUL04QhbSEt5oNbV4f7CeLLqTCw2gwDQYJKoZIhvcNAQEL
3+
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
4+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA2MjVaFw0zNDAy
5+
MjAwODA2MjVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
6+
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
7+
AQUAA4IBDwAwggEKAoIBAQDjc78SuXAmJeBc0el2/m+2lwtk3J/VrNxHYkhjHa8K
8+
/ybU89VvKGuv9+L3IP67WMguFTaMgivJYUePjfMchtNJLJ+4cR9BkBKH4JnyXDae
9+
s0a5181LxRo8rqcaOw9hmJTgt9R4dIRTR3GN2/VLhlR+L9OTYA54RUtMyMMpyk5M
10+
YIJbcOwiwkVLsIYnexXDfgz9vQGl/2vBQ/RBtDBvbSyBiWox9SuzOrya1HUBzJkM
11+
Iu5L0bSa0LAeXHT3i3P1Y4WPt9ub70OhUNfJtHC+XbGFSEkkQG+lfbXU75XLoMWa
12+
iATMREOcb3Mq+pn1G8o1ZHVc6lBHUkfrNfxs5P/GQcSvAgMBAAGjUzBRMB0GA1Ud
13+
DgQWBBQGkdK2gR2HrQTnZnbuWO7I1+wdxDAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
14+
ZnbuWO7I1+wdxDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBx
15+
G0hFtMwV/agIwC3ZaYC36ZWiijFzWkJSZG+fqAy32mSoVL2uQvOT8vEfF0ZnAcPc
16+
JI4oI059dBhAVlwqv6uLHyD4Gf2bF4oSLljdTz3X23llF+/wrTC2LLqMrm09aUC0
17+
ac74Q0FVwVJJcqH1HgemCMVjna5MkwNA6B+q7uR3eQ692VqXk6vjd4fRLBg1bBO1
18+
hXjasfNxA8A9quORF5+rjYrwyUZHuzcs0FfSClckIt4tHKtt4moLufOW6/PM4fRe
19+
AgdDfiTupxYLJFz4hFPhfgCh4TjQ+f9+uP4IAjW42dJmTVZjLEku/hm5lxCFObAq
20+
RgfaNwH8Ug1r1xswjSZG
21+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDWjCCAkKgAwIBAgIUUPCOgMA2v09E29fCkogx3RUBRtEwDQYJKoZIhvcNAQEL
3+
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
4+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA3MzFaFw0zNDAy
5+
MjAwODA3MzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
6+
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
7+
AQUAA4IBDwAwggEKAoIBAQCrNeomxI2aoP+4iUy5SiA+41oHUDZDFeJOBjv5JCsK
8+
mlvFqxE9zynmPOVpuABErOJwzerPTa4NYKvuvs5CxVJUV5CXtWANuu9majioZNzj
9+
f877MDNX/GnZHK2gnkxVrZCPaDmx9yiMsFMXgmfdrDhwoUpXbdgSyeU/al9Ds2kF
10+
0hrHOH2LBWt/mVeLbONU5CC1HOdVVw+uRlhVlxnfhTPd/Nru3rJx7R0sN7qXcZpJ
11+
PL87WvrszLVOux24DeaOz9oiD2b7egFyUuq1BM25iCwi8s/Ths8xd0Ca1d8mEcHW
12+
FVd4w2+nUMXFE+IbP+wo6FXuiSaOBNri3rztpvCCMaWjAgMBAAGjQjBAMB0GA1Ud
13+
DgQWBBSOlA+9Vfbcfy8iS4HSd4V0KPtm4jAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
14+
ZnbuWO7I1+wdxDANBgkqhkiG9w0BAQsFAAOCAQEAOmzm/MwowKTrSpMSrmfA3MmW
15+
ULzsfa25WyAoTl90ATlg4653Y7pRaNfdvVvyi2V2LlPcmc7E0rfD53t1NxjDH1uM
16+
LgFMTNEaZ9nMRSW0kMiwaRpvmXS8Eb9PXfvIM/Mw0co/aMOtAQnfTGIqsgkQwKyk
17+
1GG7QKQq3p4QGu5ZaTnjnaoa79hODt+0xQDD1wp6C9xwBY0M4gndAi3wkOeFkGv+
18+
OmGPtaCBu5V9tJCZ9dfZvjkaK44NGwDw0urAcYRK2h7asnlflu7cnlGMBB0qY4kQ
19+
BX5WI8UjN6rECBHbtNRvEh06ogDdHbxYV+TibrqkkeDRw6HX1qqiEJ+iCgWEDQ==
20+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrNeomxI2aoP+4
3+
iUy5SiA+41oHUDZDFeJOBjv5JCsKmlvFqxE9zynmPOVpuABErOJwzerPTa4NYKvu
4+
vs5CxVJUV5CXtWANuu9majioZNzjf877MDNX/GnZHK2gnkxVrZCPaDmx9yiMsFMX
5+
gmfdrDhwoUpXbdgSyeU/al9Ds2kF0hrHOH2LBWt/mVeLbONU5CC1HOdVVw+uRlhV
6+
lxnfhTPd/Nru3rJx7R0sN7qXcZpJPL87WvrszLVOux24DeaOz9oiD2b7egFyUuq1
7+
BM25iCwi8s/Ths8xd0Ca1d8mEcHWFVd4w2+nUMXFE+IbP+wo6FXuiSaOBNri3rzt
8+
pvCCMaWjAgMBAAECggEAOTWjz16AXroLmRMv8v5E9h6sN6Ni7lnCrAXDRoYCZ+Ga
9+
Ztu5wCiYPJn+oqvcUxZd+Ammu6yeS1QRP468h20+DHbSFw+BUDU1x8gYtJQ3h0Fu
10+
3VqG3ZC3odfGYNRkd4CuvGy8Uq5e+1vz9/gYUuc4WNJccAiBWg3ir6UQviOWJV46
11+
LGfdEd9hVvIGl5pmArMBVYdpj9+JHunDtG4uQxiWla5pdLjlkC2mGexD18T9d718
12+
6I+o3YHv1Y9RPT1d4rNhYQWx6YdTTD2rmS7nTrzroj/4fXsblpXzR+/l7crlNERY
13+
67RMPwgDR1NiAbCAJKsSbMS66lRCNlhTM4YffGAN6QKBgQDkIdcNm9j49SK5Wbl5
14+
j8U6UbcVYPzPG+2ea+fDfUIafA0VQHIuX6FgA17Kp7BDX9ldKtSBpr0Z8vetVswr
15+
agmXVMR/7QdvnZ9NpL66YA/BRs67CvsryVu4AVAzThFGySmlcXGlPq47doWDQ3B9
16+
0BOEnVoeDXR3SabaNsEbhDYn1wKBgQDAIAUyhJcgz+LcgaAtBwdnEN57y66JlRVZ
17+
bsb6cEG/MNmnLjQYsplJjNbz4yrB5ukTChPTGRF/JQRqHoXh6DGQFHvobukwwA6x
18+
RAIIq0NLJ5HUipfOi+VpCbWUHdoUNhwjAB2qVtD4LXE2Lyn46C8ET5eRtRjUKpzV
19+
lpsq63KHFQKBgFB+cDbpCoGtXPcxZXQy+lA9jPAKLKmXHRyMzlX32F8n7iXVe3RJ
20+
YdNS3Rt8V4EuTK/G8PxeLNL/G80ZlyiqXX/79Ol+ZOVJJHBs9K8mPejgZwkwMrec
21+
cLRYIkg3/3iOehdaE9NOboOkqi9KmGKMDJb6PlXkQXflkO3l6/UdjU45AoGAen0v
22+
sxiTncjMU1eVfn+nuY8ouXaPbYoOFXmqBItDb5i+e3baohBj6F+Rv+ZKIVuNp6Ta
23+
JNErtYstOFcDdpbp2nkk0ni71WftNhkszsgZ3DV7JS3DQV0xwvj8ulUZ757b63is
24+
cShujHu0XR5OvTGSoEX6VVxHWyVb3lTp0sBPwU0CgYBe2Ieuya0X8mAbputFN64S
25+
Kv++dqktTUT8i+tp07sIrpDeYwO3D89x9kVSJj4ImlmhiBVGkxFWPkpGyBLotdse
26+
Ai/E6f5I7CDSZZC0ZucgcItNd4Yy459QY+dFwFtT3kIaD9ml8fnqQ83J9W8DWtv9
27+
6mY9FnUUufbJcpHxN58RTw==
28+
-----END PRIVATE KEY-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDWjCCAkKgAwIBAgIUUPCOgMA2v09E29fCkogx3RUBRtAwDQYJKoZIhvcNAQEL
3+
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
4+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA2NTlaFw0zNDAy
5+
MjAwODA2NTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
6+
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
7+
AQUAA4IBDwAwggEKAoIBAQC8WWbDxnLzTSfuQaO+kQnnzbwjhUHWn58s+BIEaO8M
8+
GG6bX+8r/SH9XjMfFS36qAN3qxgRun3YoRTaHc2QByiGjf5IL4EAPDnLN+NzUIL5
9+
7Gi2QPQP/GksAsOGKWk/nMRPk1vcMptkFVIWSp474SQ0A92Z9z0dUIqBpjRa34kr
10+
HsAIcT59/EG7YBBadMk0fQIxQVLh3Vosky85q+0waFihe47Ef5U2UftexoUx4Vcz
11+
6EtP60Wx+4qN+FLsr+n2B7Oz2ITqfwgqLzjNLZwm9bMjcLZ0fWm1A/W1C989MXwI
12+
w6DAPEZv7pbgp8r9phyrNieSDuuRaCvFsaXh6troAjLxAgMBAAGjQjBAMB0GA1Ud
13+
DgQWBBRJCYAQG2+1FN5P/wyAR1AsrAyb4DAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
14+
ZnbuWO7I1+wdxDANBgkqhkiG9w0BAQsFAAOCAQEAmllul/GIH7RVq85mM/SxP47J
15+
M7Z7T032KuR3n/Psyv2iq/uEV2CUje3XrKNwR2PaJL4Q6CtoWy7xgIP+9CBbjddR
16+
M7sdNQab8P2crAUtBKnkNOl/na/5KnXnjwi/PmWJJ9i2Cqt0PPkaykTWp/MLfYIw
17+
RPkY2Yo8f8gEiqXQd+0qTuMgumbgkPq3V8Lk1ocy62F5/qUhXxH+ifAXEoUQS6EG
18+
8DlgwdZlfUY+jeM6N56WzYmxD1syjNW7faPio+qXINfpYatROhqphaMQ5SA6TRj6
19+
jcnLa31TdDdWmWYDcYgZntAv6yGi3rh0MdYqeNS0FKlMKmaH81VHs7V1UUXwUQ==
20+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8WWbDxnLzTSfu
3+
QaO+kQnnzbwjhUHWn58s+BIEaO8MGG6bX+8r/SH9XjMfFS36qAN3qxgRun3YoRTa
4+
Hc2QByiGjf5IL4EAPDnLN+NzUIL57Gi2QPQP/GksAsOGKWk/nMRPk1vcMptkFVIW
5+
Sp474SQ0A92Z9z0dUIqBpjRa34krHsAIcT59/EG7YBBadMk0fQIxQVLh3Vosky85
6+
q+0waFihe47Ef5U2UftexoUx4Vcz6EtP60Wx+4qN+FLsr+n2B7Oz2ITqfwgqLzjN
7+
LZwm9bMjcLZ0fWm1A/W1C989MXwIw6DAPEZv7pbgp8r9phyrNieSDuuRaCvFsaXh
8+
6troAjLxAgMBAAECggEACNVCggTxCCMCr+RJKxs/NS1LWPkbZNbYjrHVmnpXV6Bf
9+
s460t0HoUasUx6zlGp+9heOyvcYat8maIj6KkOodBu5q0fTUXm/0n+ivlI1ejxz8
10+
ritupr9GKWe5xrVzd6XA+SBmivWenvt2/Y+jSxica4oQ3vMe3RyVWk4yn15jXu+9
11+
7B9lNyNeZtOBr6OozHGLYw4dwWcBNv2S6wevRKfHPwn/Ch5yTH1uAskgoMxUuyK2
12+
ynNVHWUhyS4pFU7Tex5ENDel15VYdbxV/2lQ2W6fHMLtC5GWKJXXbigCX7pfOpzC
13+
BFJEfZl7ze/qptE9AR7DkLFYyMtrS7OlebYbLDOM9wKBgQD+rTdwULZibpKwlI3a
14+
9Y22d4N/EDFvuu8LnuEiVQnXgwg9M+tlaa2liP18j1a7y/FCfoXf5sjUWCsdYR6d
15+
C0TuiOGI59hYGI94NvVLAmOutR+vJ/3jhbv5wyqEQLhJ42Yz9kWBrDCI+V3q3TdO
16+
H7wcH6suUIZpeLEJF4qHzY/1dwKBgQC9U/Pvswiww8sfysmd5shUNo4ofAZnTM1A
17+
ak6pWE3lSyiOkSm+3B2GqxYWLRoo1v+pTyhhXDtRRmxGtMNrKCsmlHef/o3c6kkG
18+
cuC2h/DiSmoITHy3BYKJoDeE54E8ubXUUKqHo41LYUs+D7M/IGxeiO13MUoIrEtF
19+
AwzVWPBU1wKBgH8barD2x6Bm+XWCHy6qIZlxGsMfDN1r2gTdvhWJhcj3D/Sj5heO
20+
X+lfbsxtKee+yOHcDesK3y8D9jjKkSHmTvgSfyX6OML3NxvTqidOwPugUHj2J8QX
21+
qhLk8mJhftj50reacWRf0TV76ADhecnXEuaic6hA7mTTpOAZzL0svm3PAoGBALWF
22+
r6VLX3KzVqZVtLb7FWmAoQ35093pCgXPpznAW3cTd4Axd/fxbTG4CUYb2i/760X2
23+
ij3Gw2yqe5fTKmYsLisgQA2bb4K28msHa6I2dmNQe5cXVp/X3Y98mJ6JpCSH3ekB
24+
qm7ABfGXCCApx28n9B8zY5JbJKNqJgS15vELA+ojAoGAAkaV2w46+3iQ6gJtQepr
25+
zGNybiYBx/Wo5fDdTS5u0xN+ZdC9fl2Zs0n7sMmUT8bWdDLcMnntHHO+oDIKyRHs
26+
TQh1n68vQ4JoegQv3Z9Z/TLEKqr9gyJC1Ao6M4bZpPhUWQwupfHColtsr2TskcnJ
27+
Nf2FpJZ7z6fQEShGlK1yTXM=
28+
-----END PRIVATE KEY-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
dependencies:
2+
espressif/libwebsockets:
3+
version: "*"
4+
override_path: "../../../"
5+
protocol_examples_common:
6+
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
/* ESP libwebsockets server example
7+
8+
This example code is in the Public Domain (or CC0 licensed, at your option.)
9+
10+
Unless required by applicable law or agreed to in writing, this
11+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
CONDITIONS OF ANY KIND, either express or implied.
13+
*/
14+
15+
#include <libwebsockets.h>
16+
#include <stdio.h>
17+
18+
#include "esp_wifi.h"
19+
#include "esp_system.h"
20+
#include "nvs_flash.h"
21+
#include "protocol_examples_common.h"
22+
23+
#include "freertos/FreeRTOS.h"
24+
#include "freertos/task.h"
25+
26+
#include "esp_log.h"
27+
#include "esp_netif.h"
28+
#include "esp_netif_ip_addr.h"
29+
30+
#define RING_DEPTH 4096
31+
#define LWS_MAX_PAYLOAD 1024
32+
33+
static int callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason,
34+
void *user, void *in, size_t len);
35+
/* one of these created for each message */
36+
struct msg {
37+
void *payload; /* is malloc'd */
38+
size_t len;
39+
};
40+
41+
/* one of these is created for each client connecting to us */
42+
43+
struct per_session_data__minimal {
44+
struct per_session_data__minimal *pss_list;
45+
struct lws *wsi;
46+
int last; /* the last message number we sent */
47+
unsigned char buffer[RING_DEPTH];
48+
size_t buffer_len;
49+
int is_receiving_fragments;
50+
int is_ready_to_send;
51+
};
52+
53+
/* one of these is created for each vhost our protocol is used with */
54+
55+
struct per_vhost_data__minimal {
56+
struct lws_context *context;
57+
struct lws_vhost *vhost;
58+
const struct lws_protocols *protocol;
59+
60+
struct per_session_data__minimal *pss_list; /* linked-list of live pss*/
61+
62+
struct msg amsg; /* the one pending message... */
63+
int current; /* the current message number we are caching */
64+
};
65+
66+
static struct lws_protocols protocols[] = {
67+
{
68+
.name = "lws-minimal-server-echo",
69+
.callback = callback_minimal_server_echo,
70+
.per_session_data_size = sizeof(struct per_session_data__minimal),
71+
.rx_buffer_size = RING_DEPTH,
72+
.id = 0,
73+
.user = NULL,
74+
.tx_packet_size = RING_DEPTH
75+
},
76+
LWS_PROTOCOL_LIST_TERM
77+
};
78+
79+
static int options;
80+
static const char *TAG = "lws-server-echo", *iface = "";
81+
82+
/* pass pointers to shared vars to the protocol */
83+
static const struct lws_protocol_vhost_options pvo_options = {
84+
NULL,
85+
NULL,
86+
"options", /* pvo name */
87+
(void *) &options /* pvo value */
88+
};
89+
90+
static const struct lws_protocol_vhost_options pvo_interrupted = {
91+
&pvo_options,
92+
NULL,
93+
"interrupted", /* pvo name */
94+
NULL /* pvo value */
95+
};
96+
97+
static const struct lws_protocol_vhost_options pvo = {
98+
NULL, /* "next" pvo linked-list */
99+
&pvo_interrupted, /* "child" pvo linked-list */
100+
"lws-minimal-server-echo", /* protocol name we belong to on this vhost */
101+
"" /* ignored */
102+
};
103+
104+
int app_main(int argc, const char **argv)
105+
{
106+
ESP_LOGI(TAG, "[APP] Startup..");
107+
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
108+
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
109+
esp_log_level_set("*", ESP_LOG_INFO);
110+
111+
ESP_ERROR_CHECK(nvs_flash_init());
112+
ESP_ERROR_CHECK(esp_netif_init());
113+
ESP_ERROR_CHECK(esp_event_loop_create_default());
114+
115+
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
116+
* Read "Establishing Wi-Fi or Ethernet Connection" section in
117+
* examples/protocols/README.md for more information about this function.
118+
*/
119+
ESP_ERROR_CHECK(example_connect());
120+
121+
/* Create LWS Context - Server. */
122+
struct lws_context_creation_info info;
123+
struct lws_context *context;
124+
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
125+
126+
lws_set_log_level(logs, NULL);
127+
ESP_LOGI(TAG, "LWS minimal ws server echo\n");
128+
129+
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
130+
info.port = CONFIG_WEBSOCKET_PORT;
131+
info.iface = iface;
132+
info.protocols = protocols;
133+
info.pvo = &pvo;
134+
info.pt_serv_buf_size = 64 * 1024;
135+
136+
#ifdef CONFIG_WS_OVER_TLS
137+
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
138+
139+
/* Configuring server certificates for mutual authentification */
140+
extern const char cert_start[] asm("_binary_server_cert_pem_start"); // Server certificate
141+
extern const char cert_end[] asm("_binary_server_cert_pem_end");
142+
extern const char key_start[] asm("_binary_server_key_pem_start"); // Server private key
143+
extern const char key_end[] asm("_binary_server_key_pem_end");
144+
extern const char cacert_start[] asm("_binary_ca_cert_pem_start"); // CA certificate
145+
extern const char cacert_end[] asm("_binary_ca_cert_pem_end");
146+
147+
info.server_ssl_cert_mem = cert_start;
148+
info.server_ssl_cert_mem_len = cert_end - cert_start - 1;
149+
info.server_ssl_private_key_mem = key_start;
150+
info.server_ssl_private_key_mem_len = key_end - key_start - 1;
151+
info.server_ssl_ca_mem = cacert_start;
152+
info.server_ssl_ca_mem_len = cacert_end - cacert_start;
153+
#endif
154+
155+
context = lws_create_context(&info);
156+
if (!context) {
157+
ESP_LOGE(TAG, "lws init failed\n");
158+
return 1;
159+
}
160+
161+
while (n >= 0) {
162+
n = lws_service(context, 100);
163+
}
164+
165+
lws_context_destroy(context);
166+
167+
return 0;
168+
}
169+
170+
static int callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason,
171+
void *user, void *in, size_t len)
172+
{
173+
struct per_session_data__minimal *pss = (struct per_session_data__minimal *)user;
174+
struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *)
175+
lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi));
176+
char client_address[128];
177+
178+
switch (reason) {
179+
case LWS_CALLBACK_PROTOCOL_INIT:
180+
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
181+
lws_get_protocol(wsi),
182+
sizeof(struct per_vhost_data__minimal));
183+
if (!vhd) {
184+
ESP_LOGE("LWS_SERVER", "Failed to allocate vhost data.");
185+
return -1;
186+
}
187+
vhd->context = lws_get_context(wsi);
188+
vhd->protocol = lws_get_protocol(wsi);
189+
vhd->vhost = lws_get_vhost(wsi);
190+
vhd->current = 0;
191+
vhd->amsg.payload = NULL;
192+
vhd->amsg.len = 0;
193+
break;
194+
195+
case LWS_CALLBACK_ESTABLISHED:
196+
lws_get_peer_simple(wsi, client_address, sizeof(client_address));
197+
ESP_LOGI("LWS_SERVER", "New client connected: %s", client_address);
198+
lws_ll_fwd_insert(pss, pss_list, vhd->pss_list);
199+
pss->wsi = wsi;
200+
pss->last = vhd->current;
201+
pss->buffer_len = 0;
202+
pss->is_receiving_fragments = 0;
203+
pss->is_ready_to_send = 0;
204+
memset(pss->buffer, 0, RING_DEPTH);
205+
break;
206+
207+
case LWS_CALLBACK_CLOSED:
208+
lws_get_peer_simple(wsi, client_address, sizeof(client_address));
209+
ESP_LOGI("LWS_SERVER", "Client disconnected: %s", client_address);
210+
lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, pss, vhd->pss_list);
211+
break;
212+
213+
case LWS_CALLBACK_RECEIVE:
214+
lws_get_peer_simple(wsi, client_address, sizeof(client_address));
215+
216+
bool is_binary = lws_frame_is_binary(wsi); /* Identify if it is binary or text */
217+
218+
ESP_LOGI("LWS_SERVER", "%s fragment received from %s (%d bytes)",
219+
is_binary ? "Binary" : "Text", client_address, (int)len);
220+
221+
if (lws_is_first_fragment(wsi)) { /* First fragment: reset the buffer */
222+
pss->buffer_len = 0;
223+
}
224+
225+
if (pss->buffer_len + len < RING_DEPTH) {
226+
memcpy(pss->buffer + pss->buffer_len, in, len);
227+
pss->buffer_len += len;
228+
} else {
229+
ESP_LOGE("LWS_SERVER", "Fragmented message exceeded buffer limit.");
230+
return -1;
231+
}
232+
233+
/* If it is the last part of the fragment, process the complete message */
234+
if (lws_is_final_fragment(wsi)) {
235+
ESP_LOGI("LWS_SERVER", "Complete %s message received from %s (%d bytes)",
236+
is_binary ? "binary" : "text", client_address, (int)pss->buffer_len);
237+
238+
if (!is_binary) {
239+
ESP_LOGI("LWS_SERVER", "Complete text message: %.*s", (int)pss->buffer_len, (char *)pss->buffer);
240+
} else {
241+
char hex_output[pss->buffer_len * 2 + 1]; /* Display the binary message as hexadecimal */
242+
for (int i = 0; i < pss->buffer_len; i++) {
243+
snprintf(&hex_output[i * 2], 3, "%02X", pss->buffer[i]);
244+
}
245+
ESP_LOGI("LWS_SERVER", "Complete binary message (hex): %s", hex_output);
246+
}
247+
248+
/* Respond to the client */
249+
int write_type = is_binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT;
250+
int m = lws_write(wsi, (unsigned char *)pss->buffer, pss->buffer_len, write_type);
251+
pss->buffer_len = 0;
252+
253+
if (m < (int)pss->buffer_len) {
254+
ESP_LOGE("LWS_SERVER", "Failed to send %s message.", is_binary ? "binary" : "text");
255+
return -1;
256+
}
257+
break;
258+
}
259+
260+
/* If the message is not fragmented, process JSON, echo, and other messages */
261+
262+
/* JSON */
263+
if (strstr((char *)in, "{") && strstr((char *)in, "}")) {
264+
ESP_LOGI("LWS_SERVER", "JSON message received from %s: %.*s", client_address, (int)len, (char *)in);
265+
int m = lws_write(wsi, (unsigned char *)in, len, LWS_WRITE_TEXT);
266+
if (m < (int)len) {
267+
ESP_LOGE("LWS_SERVER", "Failed to send JSON message.");
268+
return -1;
269+
}
270+
break;
271+
}
272+
273+
/* Echo */
274+
if (!is_binary) {
275+
ESP_LOGI("LWS_SERVER", "Text message received from %s (%d bytes): %.*s",
276+
client_address, (int)len, (int)len, (char *)in);
277+
int m = lws_write(wsi, (unsigned char *)in, len, LWS_WRITE_TEXT);
278+
if (m < (int)len) {
279+
ESP_LOGE("LWS_SERVER", "Failed to send text message.");
280+
return -1;
281+
}
282+
break;
283+
}
284+
285+
/* Bin */
286+
ESP_LOGI("LWS_SERVER", "Binary message received from %s (%d bytes)", client_address, (int)len);
287+
int m = lws_write(wsi, (unsigned char *)in, len, LWS_WRITE_BINARY);
288+
if (m < (int)len) {
289+
ESP_LOGE("LWS_SERVER", "Failed to send binary message.");
290+
return -1;
291+
}
292+
293+
ESP_LOGI("LWS_SERVER", "Message sent back to client.");
294+
break;
295+
296+
default:
297+
break;
298+
}
299+
300+
return 0;
301+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Unlicense OR CC0-1.0
3+
import json
4+
import random
5+
import re
6+
import ssl
7+
import string
8+
import time
9+
10+
import websocket
11+
12+
13+
def get_esp32_ip(dut):
14+
"""Retrieve ESP32 IP address from ESP-IDF logs."""
15+
ip_regex = re.compile(r'IPv4 address:\s*(\d+\.\d+\.\d+\.\d+)')
16+
timeout = time.time() + 60
17+
18+
while time.time() < timeout:
19+
try:
20+
match = dut.expect(ip_regex, timeout=5)
21+
if match:
22+
ip_address = match.group(1).decode()
23+
print(f'ESP32 IP found: {ip_address}')
24+
return ip_address
25+
except Exception:
26+
pass
27+
28+
print('Error: ESP32 IP not found in logs.')
29+
raise RuntimeError('ESP32 IP not found.')
30+
31+
32+
def wait_for_websocket_server(dut):
33+
"""Waits for the ESP32 WebSocket server to be ready."""
34+
server_ready_regex = re.compile(r'LWS minimal ws server echo')
35+
timeout = time.time() + 20
36+
37+
while time.time() < timeout:
38+
try:
39+
dut.expect(server_ready_regex, timeout=5)
40+
print('WebSocket server is up!')
41+
return
42+
except Exception:
43+
pass
44+
45+
print('Error: WebSocket server did not start.')
46+
raise RuntimeError('WebSocket server failed to start.')
47+
48+
49+
def connect_websocket(uri, use_tls):
50+
"""Attempts to connect to the WebSocket server with retries, handling TLS if enabled."""
51+
for attempt in range(5):
52+
try:
53+
print(f'Attempting to connect to {uri} (Try {attempt + 1}/5)')
54+
55+
if use_tls:
56+
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
57+
ssl_context.load_cert_chain(certfile='main/certs/client_cert.pem',
58+
keyfile='main/certs/client_key.pem')
59+
60+
try:
61+
ssl_context.load_verify_locations(cafile='main/certs/ca_cert.pem')
62+
ssl_context.verify_mode = ssl.CERT_REQUIRED
63+
ssl_context.check_hostname = False
64+
except Exception:
65+
print('Warning: CA certificate not found. Skipping mutual authentication.')
66+
ssl_context.verify_mode = ssl.CERT_NONE
67+
68+
ws = websocket.create_connection(uri, sslopt={'context': ssl_context}, timeout=10)
69+
70+
else:
71+
ws = websocket.create_connection(uri, timeout=10)
72+
73+
print('WebSocket connected successfully!')
74+
return ws
75+
except Exception as e:
76+
print(f'Connection failed: {e}')
77+
time.sleep(3)
78+
79+
print('Failed to connect to WebSocket.')
80+
raise RuntimeError('WebSocket connection failed.')
81+
82+
83+
def send_fragmented_message(ws, message, is_binary=False, fragment_size=1024):
84+
"""Sends a message in fragments."""
85+
opcode = websocket.ABNF.OPCODE_BINARY if is_binary else websocket.ABNF.OPCODE_TEXT
86+
total_length = len(message)
87+
88+
for i in range(0, total_length, fragment_size):
89+
fragment = message[i: i + fragment_size]
90+
ws.send(fragment, opcode=opcode)
91+
92+
93+
def test_examples_protocol_websocket(dut):
94+
"""Tests WebSocket communication."""
95+
96+
esp32_ip = get_esp32_ip(dut)
97+
wait_for_websocket_server(dut)
98+
99+
# Retrieve WebSocket configuration from SDKCONFIG
100+
try:
101+
port = int(dut.app.sdkconfig['WEBSOCKET_PORT']) # Gets WebSocket port
102+
use_tls = dut.app.sdkconfig.get('WS_OVER_TLS', False) # Gets TLS setting
103+
except KeyError:
104+
print('Error: WEBSOCKET_PORT or WS_OVER_TLS not found in sdkconfig.')
105+
raise
106+
107+
protocol = 'wss' if use_tls else 'ws'
108+
uri = f'{protocol}://{esp32_ip}:{port}'
109+
110+
print(f'\nWebSocket Configuration:\n - TLS: {use_tls}\n - Port: {port}\n - URI: {uri}\n')
111+
112+
ws = connect_websocket(uri, use_tls)
113+
114+
def test_echo():
115+
"""Sends and verifies an echo message."""
116+
ws.send('Hello ESP32!')
117+
response = ws.recv()
118+
assert response == 'Hello ESP32!', 'Echo response mismatch!'
119+
print('Echo test passed!')
120+
121+
def test_send_receive_long_msg(msg_len=1024):
122+
"""Sends and verifies a long text message."""
123+
send_msg = ''.join(random.choices(string.ascii_letters + string.digits, k=msg_len))
124+
print(f'Sending long message ({msg_len} bytes)...')
125+
ws.send(send_msg)
126+
response = ws.recv()
127+
assert response == send_msg, 'Long message mismatch!'
128+
print('Long message test passed!')
129+
130+
def test_send_receive_binary():
131+
"""Sends and verifies binary data."""
132+
expected_binary_data = bytearray([0, 0, 0, 0, 0, 1, 1, 1, 1, 1])
133+
print('Sending binary data...')
134+
ws.send(expected_binary_data, opcode=websocket.ABNF.OPCODE_BINARY)
135+
received_data = ws.recv()
136+
assert received_data == expected_binary_data, 'Binary data mismatch!'
137+
print('Binary data test passed!')
138+
139+
def test_json():
140+
"""Sends and verifies a JSON message."""
141+
json_data = {'id': '1', 'name': 'test_user'}
142+
json_string = json.dumps(json_data)
143+
print('Sending JSON message...')
144+
ws.send(json_string)
145+
response = ws.recv()
146+
received_json = json.loads(response)
147+
assert received_json == json_data, 'JSON data mismatch!'
148+
print('JSON test passed!')
149+
150+
def test_recv_fragmented_msg1():
151+
"""Verifies reception of the first text fragment."""
152+
print('Waiting for first fragment log...')
153+
dut.expect(re.compile(r'I \(\d+\) LWS_SERVER: .* fragment received from .* \(1024 bytes\)'), timeout=20)
154+
print('Fragmented message part 1 received correctly.')
155+
156+
def test_recv_fragmented_msg2():
157+
"""Verifies reception of the second text fragment."""
158+
print('Waiting for second fragment log...')
159+
dut.expect(re.compile(r'I \(\d+\) LWS_SERVER: .* fragment received from .* \(.* bytes\)'), timeout=20)
160+
print('Fragmented message part 2 received correctly.')
161+
162+
def test_fragmented_txt_msg():
163+
"""Tests sending and receiving a fragmented text message."""
164+
part1 = ''.join(random.choices(string.ascii_letters + string.digits, k=1024))
165+
part2 = ''.join(random.choices(string.ascii_letters + string.digits, k=976))
166+
message = part1 + part2
167+
168+
send_fragmented_message(ws, message, is_binary=False, fragment_size=1024)
169+
170+
print('Waiting for Complete text message log...')
171+
172+
escaped_message_start = re.escape(message[:30])
173+
escaped_message_end = re.escape(message[-30:])
174+
175+
dut.expect(
176+
re.compile(
177+
rb"I \(\d+\) LWS_SERVER: Complete text message:.*?" + escaped_message_start.encode() + rb".*?" + escaped_message_end.encode(),
178+
re.DOTALL
179+
),
180+
timeout=20
181+
)
182+
183+
print('Fragmented text message received correctly.')
184+
185+
def test_fragmented_binary_msg():
186+
"""Tests sending and receiving a fragmented binary message."""
187+
expected_data = bytearray([0, 0, 0, 0, 0, 1, 1, 1, 1, 1] * 5)
188+
send_fragmented_message(ws, expected_data, is_binary=True, fragment_size=10)
189+
190+
print('Waiting for Complete binary message log...')
191+
dut.expect(re.compile(r'I \(\d+\) LWS_SERVER: Complete binary message \(hex\): '), timeout=10)
192+
print('Fragmented binary message received correctly.')
193+
194+
def test_close():
195+
"""Closes WebSocket connection and verifies closure."""
196+
print('Closing WebSocket...')
197+
ws.close()
198+
199+
try:
200+
close_regex = re.compile(rb"websocket: Received closed message with code=(\d+)")
201+
disconnect_regex = re.compile(rb"LWS_SERVER: Client disconnected")
202+
203+
match = dut.expect([close_regex, disconnect_regex], timeout=5)
204+
205+
if match == 0:
206+
close_code = match.group(1).decode()
207+
print(f'WebSocket closed successfully with code: {close_code}')
208+
else:
209+
print('WebSocket closed successfully (client disconnect detected).')
210+
211+
except Exception:
212+
print('WebSocket close message not found.')
213+
raise
214+
215+
test_echo()
216+
test_send_receive_long_msg()
217+
test_send_receive_binary()
218+
test_json()
219+
test_recv_fragmented_msg1()
220+
test_recv_fragmented_msg2()
221+
test_fragmented_txt_msg()
222+
test_fragmented_binary_msg()
223+
test_close()
224+
225+
ws.close()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CONFIG_IDF_TARGET="esp32"
2+
CONFIG_IDF_TARGET_LINUX=n
3+
CONFIG_WEBSOCKET_PORT=8080
4+
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
5+
CONFIG_EXAMPLE_CONNECT_WIFI=n
6+
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
7+
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
8+
CONFIG_EXAMPLE_ETH_PHY_IP101=y
9+
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
10+
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
11+
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
12+
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
13+
CONFIG_EXAMPLE_CONNECT_IPV6=y
14+
CONFIG_WS_OVER_TLS=y
15+
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8584
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CONFIG_IDF_TARGET="esp32"
2+
CONFIG_IDF_TARGET_LINUX=n
3+
CONFIG_WEBSOCKET_PORT=8080
4+
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
5+
CONFIG_EXAMPLE_CONNECT_WIFI=n
6+
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
7+
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
8+
CONFIG_EXAMPLE_ETH_PHY_IP101=y
9+
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
10+
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
11+
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
12+
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
13+
CONFIG_EXAMPLE_CONNECT_IPV6=y
14+
CONFIG_WS_OVER_TLS=n
15+
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8584

0 commit comments

Comments
 (0)
Please sign in to comment.