This project is designed to test the functionality of the micropython-async-websocket-client package.
The project demonstrates the operation of an ESP32S controller and its clones running micropython.
Key objectives of the micropython-async-websocket-client package:
- Establish and maintain a stable WebSocket connection with a server.
- Ensure that connection failures do not disrupt the controller’s main workflow.
- Enable bidirectional communication—sending data to the server and receiving control signals from it.
- ESP32 controller
- USB cable with data wire
- Ubuntu 24.04
- Python 3.10
- VSCode + PyMakr plugin
Caution
- Use only the deb installation of VSCode! The snap version does not support the PyMakr plugin!
- The project https://github.com/Vovaman/start_ESP32_with_micropython explains how to work with ESP32 in VSCode. This includes PyMakr setup, file transfer to the controller, project synchronization, etc.
- All further steps assume that pipenv and pyenv are installed as described in the above project.
- All commands must be executed in the project’s root directory within the pipenv virtual environment.
- WiFi network
Caution
This guide assumes the ESP32 operates in a local Wi-Fi network. Examples with TLS use self-signed certificates.
Before running any examples:
-
Obtain the Wi-Fi network name () and password () used by both the computer and the controller.
-
Determine the computer’s IP address () in the Wi-Fi network.
-
Synchronize the project with the controller:
-
install the async_websocket_client package.
If the connection between the server and controller is successful, the controller’s terminal will display:
...and the server’s console output will show:
The simplest case: unencrypted communication.
src/config.json
:{ "wifi": { "SSID": "<wifi network name>", "password": "<wifi network pwd>", "attempts": 3, "delay_in_msec": 200 }, "server": "ws://<host IP>:8000/", "socket_delay_ms": 5 }
- Update project on the controller:
$ mpremote fs cp src/* :/
- Reboot the controller.
- Start the server:
$ python server.py
Caution
TLS encrypts the channel even without certificates, but this is still insecure!
-
src/config.json:
{ "wifi": { "SSID": "<wifi network name>", "password": "<wifi network pwd>", "attempts": 3, "delay_in_msec": 200 }, "server": "wss://<host IP>:8443/", "ssl": { "cert_reqs": 0 }, "socket_delay_ms": 5 }
-
Even though certificate checks are disabled, the server must still use a certificate and key.
Generate certificates:
$ ./gen_crt.sh --srv=<host IP>
Note
The gen_crt.sh script will prompt for a key password. Use the same password throughout.
The script copies required files to the project directory. Since the controller does not need certificates here, delete unnecessary files:
$ rm src/*.crt src/*.key src/ca.der
- Update the project on the controller:
$ mpremote fs cp src/* :/
- Reboot the controller.
- Start the server:
$ python server.py --ssl --ssl-keyfile=tls/<host IP>/<host IP>.key --ssl-certfile=tls/<host IP>/<host IP>.crt --port=8443
Caution
The server does not require a CA certificate in this mode.
Caution
Higher security: the controller verifies the server’s certificate, but the server allows any client.
Still insecure.
-
src/config.json
:{ "wifi": { "SSID": "<wifi network name>", "password": "<wifi network pwd>", "attempts": 3, "delay_in_msec": 200 }, "server": "wss://<host IP>:8443/", "ssl": { "cert_reqs": 2, "ca": "ca.der" }, "socket_delay_ms": 5 }
-
Generate certificates:
$ ./gen_crt.sh --srv=<host IP>
Remove unnecessary files:
$ rm src/*.crt src/*.key
-
The controller lacks a real-time clock (RTC) and defaults to January 1, 2000. If the system time is incorrect, certificate validation will fail with
The certificate validity starts in the future
.Set the controller’s time via its console:
>>> from machine import RTC >>> rtc = RTC() >>> rtc.datetime((2025,5,7,1,13,0,0,0)) # replace with current time
-
Update the project on the controller:
$ mpremote fs cp src/* :/
-
Reboot the controller.
Caution
Time may reset after reboot. If this occurs frequently, add time-setting commands to the project code.
- Start the server:
$ python server.py --ssl --ssl-keyfile=tls/<host IP>/<host IP>.key --ssl-certfile=tls/<host IP>/<host IP>.crt --port=8443
This mode is realized too, but we will not check it.
Most secure mode: enables client authentication via certificates.
- Generate certificates with a client name:
$ ./gen_crt.sh --srv=<host IP> --cn="ESP Client 1"
src/config.json
:{ "wifi": { "SSID": "<wifi network name>", "password": "<wifi network pwd>", "attempts": 3, "delay_in_msec": 200 }, "server": "wss://<host IP>:8443/", "ssl": { "key": "ESP Client 1.key", "cert": "ESP Client 1.crt", "ca": "ca.der", "cert_reqs": 2 }, "socket_delay_ms": 5 }
- Set the controller’s time (as described earlier).
- Update the project on the controller.
- Reboot the controller.
- Start the server:
$ python server.py --ssl --ssl-keyfile=tls/<host IP>/<host IP>.key --ssl-certfile=tls/<host IP>/<host IP>.crt --ssl-ca-cert=tls/ca/ca.crt --ssl-certs-reqs=2 --port=8443
The server extracts the client’s name from the certificate:
This allows certificate-based client authorization on the server.
The WebSocket server is implemented in server.py
.
It accepts connections, echoes received messages.
The src
folder contains files for the ESP32 project. Key functionalities:
- Main loop: Blinks the onboard blue LED (if available) and sends
SOS!
to the server. - Data loop: Receives server messages.
Note
Tested with firmware v1.25.0.
{
"wifi": {
"SSID": "SSID", # network name
"password": "***********", # network password
"attempts": 3, # connection attempts per cycle
"delay_in_msec": 200 # post-connection delay (ms)
},
"server": "ws://192.168.1.100:8000/", # server address
"socket_delay_ms": 5 # Read/write delay (increase for slow networks)
"ssl": {
"ca": "<ca cert in DER-format file name>", # CA certificate (DER format)
"key": "<client key>", # client private key
"cert": "<client cet>", # client certificate
"cert_reqs": 2 # certificate check mode: 0 - CERT_NONE, 1 - CERT_OPTIONAL, 2 - CERT_REQUIRED
}
}
All components function correctly. The ESP32 maintains communication with the server, and the server echoes messages to clients.
Test scenarios:
- Power cycle the controller.
- Stop/restart the server.
- Send rapid bursts of messages.
- Adjust
socket_delay_ms
insrc/config.json
. - Experiment with TLS settings.
- ...