Version 0.4.2. Removed the components/websocket server and switched to the official http and websockets server. This server will also recover if the wifi connection disconnects and reconnects. idf 5.1 tip

Wed, 03 Jul 2024 20:01:31 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 03 Jul 2024 20:01:31 +0200
branch
idf 5.1
changeset 142
1f7069278fe7
parent 141
e117e7462096

Version 0.4.2. Removed the components/websocket server and switched to the official http and websockets server. This server will also recover if the wifi connection disconnects and reconnects.

CMakeLists.txt file | annotate | diff | comparison | revisions
README.md file | annotate | diff | comparison | revisions
components/websocket/CMakeLists.txt file | annotate | diff | comparison | revisions
components/websocket/Kconfig file | annotate | diff | comparison | revisions
components/websocket/README.md file | annotate | diff | comparison | revisions
components/websocket/component.mk file | annotate | diff | comparison | revisions
components/websocket/include/websocket.h file | annotate | diff | comparison | revisions
components/websocket/include/websocket_server.h file | annotate | diff | comparison | revisions
components/websocket/websocket.c file | annotate | diff | comparison | revisions
components/websocket/websocket_server.c file | annotate | diff | comparison | revisions
image/version.txt file | annotate | diff | comparison | revisions
image/w/webui.html file | annotate | diff | comparison | revisions
main/automation.c file | annotate | diff | comparison | revisions
main/brewboard.c file | annotate | diff | comparison | revisions
main/config.h file | annotate | diff | comparison | revisions
main/task_ds18b20.c file | annotate | diff | comparison | revisions
main/task_http.c file | annotate | diff | comparison | revisions
main/task_http.h file | annotate | diff | comparison | revisions
main/task_sound.c file | annotate | diff | comparison | revisions
main/task_tft.c file | annotate | diff | comparison | revisions
sdkconfig file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Mon Jul 01 08:38:57 2024 +0200
+++ b/CMakeLists.txt	Wed Jul 03 20:01:31 2024 +0200
@@ -2,9 +2,9 @@
 # in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.16)
 
-set(PROJECT_VER "0.4.1")
+set(PROJECT_VER "0.4.2")
 set(PROJECT_NAME "brewboard")
 
-set(EXTRA_COMPONENT_DIRS components/esp32-ds18b20 components/esp32-owb components/PID components/spidriver components/tft components/websocket)
+set(EXTRA_COMPONENT_DIRS components/esp32-ds18b20 components/esp32-owb components/PID components/spidriver components/tft)
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(brewboard)
--- a/README.md	Mon Jul 01 08:38:57 2024 +0200
+++ b/README.md	Wed Jul 03 20:01:31 2024 +0200
@@ -31,7 +31,7 @@
 * WiFi
 * BlueTooth, maar wordt niet gebruikt.
 * Timers en een Realtime Klok.
-* 2 SPI bussen (voor de TFT en SD/MMC).
+* 3 SPI bussen (voor het intern geheugen, de TFT en touchscreen en SD/MMC).
 * OTA, Over The Air updates.
 * Nog veel meer bussen die we niet gebruiken.
 
@@ -71,10 +71,6 @@
 De 1-wire bus: https://www.github.com/DavidAntliff/esp32-owb
 De DS18B20 sensoren: https://www.github.com/DavidAntliff/esp32-ds18b20
 
-De webserver en websockets server. Websockets worden gebruikt voor de web
-client. Deze web client probeert een kopie te zijn van het touch scherm.
-https://github.com/Molorius/esp32-websocket.git components/websocket
-
 7 Segments display font voor de web applicatie: http://www.keshikan.net
 Deze is gemaakt door keshikan.
 
@@ -83,6 +79,11 @@
 meeste boards hebben niet genoeg aansluitingen naar buiten, je hebt echt de
 36 pens versie van Geekcreit® nodig.
 
+Hetzelfde geld voor de TFT en Touch scherm. Deze wordt aangesloten op de
+eerste vrije SPI bus, dat is SPI2 aka HSPI. Dus schermpjes met andere
+bussen zoals I2C, I2S, parallel werken niet. Wil je die toch gebruiken,
+dan zul je die zelf werkend moeten maken.
+
 
 #ESP IDF.
 
@@ -95,6 +96,11 @@
 Om over te gaan naar deze versie is het belangrijk om onder de oude IDF eerst een
 update te doen naar die versie 0.3.25. Lees UPGRADE.md die in die versie zit.
 
+De IDF versies tot en met 4.x.x waren nog flink in ontwikkeling en er waren heel
+regelmatig API wijzigingen waardoor je niet makkelijk van versie kon wisselen.
+Nu met versie 5.x is dit al veel stabieler en minder kritisch om een "verkeerde"
+versie te gebruiken.
+
 ----------------------------------------------------------------------------------
 
 HENDI:
--- a/components/websocket/CMakeLists.txt	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-idf_component_register(SRCS websocket.c  websocket_server.c INCLUDE_DIRS include REQUIRES mbedtls)
-
--- a/components/websocket/Kconfig	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-
-menu "WebSocket Server"
-
-config WEBSOCKET_SERVER_MAX_CLIENTS
-  int "Max clients"
-  range 1 1000
-  default 10
-  help
-    Maximum number of clients that the WebSocket
-    server can handle at a time.
-
-config WEBSOCKET_SERVER_QUEUE_SIZE
-  int "Queue read size"
-  range 1 100
-  default 10
-  help
-    Size of the queue to deal with incoming
-    WebSocket messages. The queue holds the
-    connection, not the actual message.
-
-config WEBSOCKET_SERVER_QUEUE_TIMEOUT
-  int "Queue timeout"
-  range 0 10000
-  default 30
-  help
-    Timeout for adding new connections to the
-    read queue.
-
-config WEBSOCKET_SERVER_TASK_STACK_DEPTH
-  int "Stack depth"
-  range 3000 20000
-  default 7000
-  help
-    Stack depth for the WebSocket server. The task
-    handles reads.
-
-config WEBSOCKET_SERVER_TASK_PRIORITY
-  int "Priority"
-  range 1 20
-  default 5
-  help
-    Priority for the WebSocket server. The task
-    handles reads.
-
-
-endmenu
--- a/components/websocket/README.md	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-
-By Blake Felt - blake.w.felt@gmail.com
-
-ESP32 WebSocket
-==================
-
-A component for WebSockets on ESP-IDF using lwip netconn.
-For an example, see https://github.com/Molorius/ESP32-Examples.
-
-To add to a project, type:
-`git submodule add https://github.com/Molorius/esp32-websocket.git components/websocket`
-into the base directory of your project.
-
-Some configuration options for the Server can be found in menuconfig in:
-Component config ---> WebSocket Server
-
-This presently only has the WebSocket server code working, but client code will be added in the future (the groundwork is there).
-
-The code only allows one WebSocket server at a time, but this merely handles all incoming reads. New connections are added externally, so this can be used to hold various WebSocket connections.
-
-While this can theoretically handle very large messages, hardware constraints (RAM) limits the size of messages. I highly recommend not using more than 5000 bytes per message, but no constraint is in place for this. 
-
-Any suggestions or fixes are gladly appreciated.
-
-Table of Contents
-=================
-* [Enumerations](#enumerations)
-  * [WEBSOCKET_TYPE_t](#enum-websocket_type_t)
-* [Functions](#functions)
-  * [ws_server_start](#int-ws_server_start)
-  * [ws_server_stop](#int-ws_server_stop)
-  * [ws_server_add_client](#int-ws_server_add_clientstruct-netconn-connchar-msguint16_t-lenchar-urlvoid-callback)
-  * [ws_server_add_client_protocol](#int-ws_server_add_client_protocolstruct-netconn-connchar-msguint16_t-lenchar-urlchar-protocolvoid-callback)
-  * [ws_server_len_url](#int-ws_server_len_urlchar-url)
-  * [ws_server_len_all](#int-ws_server_len_all)
-  * [ws_server_remove_client](#int-ws_server_remove_clientint-num)
-  * [ws_server_remove_clients](#int-ws_server_remove_clientschar-url)
-  * [ws_server_remove_all](#int-ws_server_remove_all)
-  * [ws_server_send_text_client](#int-ws_server_send_text_clientint-numchar-msguint64_t-len)
-  * [ws_server_send_text_clients](#int-ws_server_send_text_clientschar-urlchar-msguint64_t-len)
-  * [ws_server_send_text_all](#int-ws_server_send_text_allchar-msguint64_t-len)
-  * [ws_server_send_text_client_from_callback](#int-ws_server_send_text_client_from_callbackint-numchar-msguint64_t-len)
-  * [ws_server_send_text_clients_from_callback](#int-ws_server_send_text_clients_from_callbackchar-urlchar-msguint64_t-len)
-  * [ws_server_send_text_all_from_callback](#int-ws_server_send_text_all_from_callbackchar-msguint64_t-len)
-
-Enumerations
-============
-
-enum WEBSOCKET_TYPE_t
----------------------
-
-The different types of WebSocket events.
-
-*Values*
-  * `WEBSOCKET_CONNECT`: A new client has successfully connected.
-  * `WEBSOCKET_DISCONNECT_EXTERNAL`: The other side sent a disconnect message.
-  * `WEBSOCKET_DISCONNECT_INTERNAL`: The esp32 server sent a disconnect message.
-  * `WEBSOCKET_DISCONNECT_ERROR`: Disconnect due to a connection error.
-  * `WEBSOCKET_TEXT`: Incoming text.
-  * `WEBSOCKET_BIN`: Incoming binary.
-  * `WEBSOCKET_PING`: The other side sent a ping message.
-  * `WEBSOCKET_PONG`: The other side successfully replied to our ping.
-
-Functions
-=========
-
-int ws_server_start()
----------------------
-
-Starts the WebSocket Server. Use this function before attempting any
-sort of transmission or adding a client.
-
-*Returns*
-  * 1: successful start
-  * 0: server already running
-
-int ws_server_stop()
---------------------
-
-Stops the WebSocket Server. New clients can still be added and
-messages can be sent, but new messages will not be received.
-
-*Returns*
-  * 1: successful stop
-  * 0: server was not running before
-
-int ws_server_add_client(struct netconn* conn,char* msg,uint16_t len,char* url,void *callback)
-----------------------------------------------------------------------------------------------
-
-Adds a client to the WebSocket Server handler and performs the necessary handshake.
-
-*Parameters*
-  * `conn`: the lwip netconn connection.
-  * `msg`: the entire incoming request message to join the server. Necessary for the handshake.
-  * `len`: the length of `msg`.
-  * `url`: the NULL-terminated url. Used to keep track of clients, not required.
-  * `callback`: the callback that is used to run WebSocket events. This must be with parameters(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) where "num" is the client number, "type" is the event type, "msg" is the incoming message, and "len" is the message length. The callback itself is optional.
-
-*Returns*
-  * -2: not enough information in `msg` to perform handshake.
-  * -1: server full, or connection issue.
-  * 0 or greater: connection number
-
-int ws_server_add_client_protocol(struct netconn* conn,char* msg,uint16_t len,char* url,char* protocol,void *callback)
-----------------------------------------------------------------------------------------------------------------------
-
-Adds a client to the WebSocket Server handler and performs the necessary handshake. Will also send
-the specified protocol.
-
-*Parameters*
-  * `conn`: the lwip netconn connection.
-  * `msg`: the entire incoming request message to join the server. Necessary for the handshake.
-  * `len`: the length of `msg`.
-  * `url`: the NULL-terminated url. Used to keep track of clients, not required.
-  * `protocol`: the NULL-terminated protocol. This will be sent to the client in the header.
-  * `callback`: the callback that is used to run WebSocket events. This must be with parameters(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) where "num" is the client number, "type" is the event type, "msg" is the incoming message, and "len" is the message length. The callback itself is optional.
-
-*Returns*
-  * -2: not enough information in `msg` to perform handshake.
-  * -1: server full, or connection issue.
-  * 0 or greater: connection number
-
-int ws_server_len_url(char* url)
---------------------------------
-
-Returns the number of clients connected to the specified URL.
-
-*Parameters*
-  * `url`: the NULL-terminated string of the desired URL.
-
-*Returns*
-  * The number of clients connected to the specified URL.
-
-int ws_server_len_all()
------------------------
-
-*Returns*
-  * The number of connected clients.
-
-int ws_server_remove_client(int num)
-------------------------------------
-
-Removes the desired client.
-
-*Parameters*
-  * `num`: the client number
-
-*Returns*
-  * 0: not a valid client number
-  * 1: client disconnected
-
-int ws_server_remove_clients(char* url)
----------------------------------------
-
-Removes all clients connect to the desired URL.
-
-*Parameters*
-  * `url`: the NULL-terminated URL.
-
-*Returns*
-  * The number of clients that were disconnected.
-
-int ws_server_remove_all()
---------------------------
-
-Removes all clients from server.
-
-*Returns*
-  * The number of clients that were disconnected.
-
-int ws_server_send_text_client(int num,char* msg,uint64_t len)
---------------------------------------------------------------
-
-Sends the desired message to the client.
-
-*Parameters*
-  * `num`: the client's number.
-  * `msg`: the desired message.
-  * `len`: the length of the message.
-
-*Returns*
-  * 0: message not sent properly
-  * 1: message sent
-
-int ws_server_send_text_clients(char* url,char* msg,uint64_t len)
------------------------------------------------------------------
-
-Sends the message to clients connected to the desired URL.
-
-*Parameters*
-  * `url`: the NULL-terminated URL.
-  * `msg`: the desired message.
-  * `len`: the length of the message.
-
-*Returns*
-  * The number of clients that the message was sent to.
-
-int ws_server_send_text_all(char* msg,uint64_t len)
----------------------------------------------------
-
-Sends the message to all connected clients.
-
-*Parameters*
-  * `msg`: the desired message
-  * `len`: the length of the message
-
-*Returns*
-  * The number of clients that the message was sent to.
-
-int ws_server_send_text_client_from_callback(int num,char* msg,uint64_t len)
-----------------------------------------------------------------------------
-
-Sends the desired message to the client. Only use this inside the callback function.
-
-*Parameters*
-  * `num`: the client's number.
-  * `msg`: the desired message.
-  * `len`: the length of the message.
-
-*Returns*
-  * 0: message not sent properly
-  * 1: message sent
-
-int ws_server_send_text_clients_from_callback(char* url,char* msg,uint64_t len)
--------------------------------------------------------------------------------
-
-Sends the message to clients connected to the desired URL. Only use this inside the callback function.
-
-*Parameters*
-  * `url`: the NULL-terminated URL.
-  * `msg`: the desired message.
-  * `len`: the length of the message.
-
-*Returns*
-  * The number of clients that the message was sent to.
-
-int ws_server_send_text_all_from_callback(char* msg,uint64_t len)
------------------------------------------------------------------
-
-Sends the message to all connected clients. Only use this inside the callback function.
-
-*Parameters*
-  * `msg`: the desired message
-  * `len`: the length of the message
-
-*Returns*
-  * The number of clients that the message was sent to.
--- a/components/websocket/component.mk	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# websocket component makefile
-# all files are in default positions
--- a/components/websocket/include/websocket.h	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-/**
- * @file websocket.h
- * @brief Websocket functions.
- * @author Blake Felt - blake.w.felt@gmail.com`
- */
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WEBSOCKET_H
-#define WEBSOCKET_H
-
-#include "lwip/api.h"
-
-
-/**
- * @brief the different codes for the callbacks
- */
-typedef enum {
-  WEBSOCKET_CONNECT,			///< Connect
-  WEBSOCKET_DISCONNECT_EXTERNAL, 	///< the other side disconnected
-  WEBSOCKET_DISCONNECT_INTERNAL, 	///< the esp32 disconnected
-  WEBSOCKET_DISCONNECT_ERROR, 		///< disconnect due to error
-  WEBSOCKET_TEXT,			///< Text message
-  WEBSOCKET_BIN,			///< Binary message
-  WEBSOCKET_PING,			///< PING message
-  WEBSOCKET_PONG			///< PONG message
-} WEBSOCKET_TYPE_t;
-
-
-
-/**
- * @brief websocket operation codes
- */
-typedef enum {
-  WEBSOCKET_OPCODE_CONT  = 0x0,		///< Continue
-  WEBSOCKET_OPCODE_TEXT  = 0x1,		///< Text
-  WEBSOCKET_OPCODE_BIN   = 0x2,		///< Binary
-  WEBSOCKET_OPCODE_CLOSE = 0x8,		///< Close connection
-  WEBSOCKET_OPCODE_PING  = 0x9,		///< PING message
-  WEBSOCKET_OPCODE_PONG  = 0xA		///< PONG message
-} WEBSOCKET_OPCODES_t;
-
-
-/**
- * @brief the header, useful for creating and quickly passing to functions
- */
-typedef struct {
-  union {
-    struct {
-      uint16_t LEN:7;     		///< bits 0..  6
-      uint16_t MASK:1;    		///< bit  7
-      uint16_t OPCODE:4;  		///< bits 8..  11
-      uint16_t :3;        		///< bits 12.. 14 reserved
-      uint16_t FIN:1;     		///< bit  15
-    } bit;
-    struct {
-      uint16_t ONE:8;     		///< bits 0..  7
-      uint16_t ZERO:8;    		///< bits 8..  15
-    } pos;
-  } param; 				///< the initial parameters of the header
-  uint64_t length; 			///< actual message length
-  union {
-    char part[4]; 			///< the mask, array
-    uint32_t full; 			///< the mask, all 32 bits
-  } key; 				///< masking key
-  bool received; 			///< was a message successfully received?
-} ws_header_t;
-
-
-/**
- * @brief A client, with space for a server callback or a client callback (depending on use)
- */
-typedef struct {
-  struct netconn* conn; 		///< the connection
-  bool connected;			///< connection state
-  char* url;            		///< the associated url,  null terminated
-  char* protocol;			///< the associated protocol, null terminated
-  bool ping;            		///< did we send a ping?
-  WEBSOCKET_OPCODES_t last_opcode; 	///< the previous opcode
-  char* contin;         		///< any continuation piece
-  bool contin_text;     		///< is the continue a binary or text?
-  uint64_t len;         		///< length of continuation
-  uint32_t unfinished;      		///< sometimes netconn doesn't read a full frame, treated similarly to a continuation frame
-  void (*ccallback)(WEBSOCKET_TYPE_t type,char* msg,uint64_t len); 		///< client callback
-  void (*scallback)(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len); 	///< server callback
-} ws_client_t;
-
-
-/**
- * @brief returns the populated client struct
- *        does not send any header, assumes the proper handshake has already occurred
- * @param conn The network connection
- * @param url The connection url.
- * @param ccallback  callback for client (userspace)
- * @param scallback  callback for server (userspace)
- * @return The Websocket client structure.
- */
-ws_client_t ws_connect_client(struct netconn* conn,
-                              char* url,
-                              void (*ccallback)(WEBSOCKET_TYPE_t type,char* msg,uint64_t len),
-                              void (*scallback)(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len)
-                             );
-
-
-/**
- * @brief Disconnect a websocket client.
- * @param client The ws_client_t structure with the client information.
- */
-void ws_disconnect_client(ws_client_t* client);
-
-/**
- * @brief Test if the client is connected.
- *        status updates after send/read/connect/disconnect.
- * @param client The ws_client_t structure with the client information.
- * @return True if connected, false if not.
- */
-bool ws_is_connected(ws_client_t client);
-
-/**
- * @brief Send data via websocjet to a client. This function performs the masking.
- * @param client The ws_client_t structure with the client information.
- * @param opcode The opcode to send the message.
- * @param msg The message itself.
- * @param len The length of the message.
- * @param mask Encrypt??
- * @return error code or ERR_OK
- */
-err_t ws_send(ws_client_t* client,WEBSOCKET_OPCODES_t opcode,char* msg,uint64_t len,bool mask);
-
-/**
- * @brief Receive a message from a websocket connection.
- * @param client The ws_client_t structure with the client information.
- * @param header unmasks and returns message. populates header.
- * @return Unmasks and returns message. populates header.
- */
-char* ws_read(ws_client_t* client,ws_header_t* header);
-
-/**
- * @brief Create a handshake hashed string.
- * @param key The key for the hash.
- * @param len The length.
- * @return The string of output.
- */
-char* ws_hash_handshake(char* key,uint8_t len);
-
-#endif // ifndef WEBSOCKET_H
-
-#ifdef __cplusplus
-}
-#endif
--- a/components/websocket/include/websocket_server.h	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/**
- * @file websocket_server.h
- * @brief Websocket server functions.
- * @author Blake Felt - blake.w.felt@gmail.com`
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WEBSOCKET_SERVER_H
-#define WEBSOCKET_SERVER_H
-
-#include "websocket.h"
-
-#define WEBSOCKET_SERVER_MAX_CLIENTS CONFIG_WEBSOCKET_SERVER_MAX_CLIENTS		///< Maximum allowed clients
-#define WEBSOCKET_SERVER_QUEUE_SIZE CONFIG_WEBSOCKET_SERVER_QUEUE_SIZE			///< Webserver requests queue size
-#define WEBSOCKET_SERVER_QUEUE_TIMEOUT CONFIG_WEBSOCKET_SERVER_QUEUE_TIMEOUT		///< Queue timeout
-#define WEBSOCKET_SERVER_TASK_STACK_DEPTH CONFIG_WEBSOCKET_SERVER_TASK_STACK_DEPTH	///< Task stack size
-#define WEBSOCKET_SERVER_TASK_PRIORITY CONFIG_WEBSOCKET_SERVER_TASK_PRIORITY		///< Task priority
-#define WEBSOCKET_SERVER_PINNED CONFIG_WEBSOCKET_SERVER_PINNED				///< Pinned to a CPU
-#if WEBSOCKET_SERVER_PINNED
-#define WEBSOCKET_SERVER_PINNED_CORE CONFIG_WEBSOCKET_SERVER_PINNED_CORE		///< If pinned, which CPU
-#endif
-
-/**
- * @brief Starts the server.
- * @return 1 if started, 0 if the server was already started.
- */
-int ws_server_start();
-
-/**
- * @brief Stops the server.
- * @return 1 if stopped, 0 if the server was not running.
- */
-int ws_server_stop();
-
-/**
- * @brief Adds a client, returns the client's number in the server.
- * @param conn The network connection.
- * @param msg The message received from the client.
- * @param len The length of the message.
- * @param url The url of the connection.
- * @param callback The callback function.
- * @return Negative if an error, else the connection number.
- */
-int ws_server_add_client(struct netconn* conn,
-                         char* msg,
-                         uint16_t len,
-                         char* url,
-                         void (*callback)(uint8_t num, WEBSOCKET_TYPE_t type, char* msg, uint64_t len));
-
-/**
- * @brief Adds a client, returns the client's number in the server.
- * @param conn The network connection.
- * @param msg The message received from the client.
- * @param len The length of the message.
- * @param url The url of the connection.
- * @param protocol The protocol requested by the client.
- * @param callback The callback function.
- * @return Negative if an error, else the connection number.
- */
-int ws_server_add_client_protocol(struct netconn* conn,
-                                  char* msg,
-                                  uint16_t len,
-                                  char* url,
-                                  char* protocol,
-                                  void (*callback)(uint8_t num, WEBSOCKET_TYPE_t type, char* msg, uint64_t len));
-
-/**
- * @brief Returns the number of connected clients to url
- * @param url The url to check.
- * @return The number of connected clients.
- */
-int ws_server_len_url(char* url);
-
-/**
- * @brief Returns the total number of connected clients.
- * @return The number of connected clients.
- */
-int ws_server_len_all();
-
-/**
- * @brief Removes the client with the set number
- * @param num The client number.
- * @return 1 if success, else 0.
- */
-int ws_server_remove_client(int num);
-
-/**
- * @brief Removes all clients connected to the specified url.
- * @param url The connection url.
- * @return The number of clients removed.
- */
-int ws_server_remove_clients(char* url);
-
-/**
- * @brief Removes all clients from the server.
- * @return The number of clients removed.
- */
-int ws_server_remove_all();
-
-/**
- * @brief Send text to client with the set number.
- * @param num The client connection number.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return 1 if message send.
- */
-int ws_server_send_text_client(int num,char* msg,uint64_t len);
-int ws_server_send_text_client_from_callback(int num,char* msg,uint64_t len);
-
-/**
- * @brief Sends text to all clients with the set url.
- * @param url The clients url.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return The number of clients the message was send to.
- */
-int ws_server_send_text_clients(char* url,char* msg,uint64_t len);
-int ws_server_send_text_clients_from_callback(char* url,char* msg,uint64_t len);
-
-/**
- * @brief Sends text to all clients.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return The number of clients the message was send to.
- */
-int ws_server_send_text_all(char* msg,uint64_t len);
-int ws_server_send_text_all_from_callback(char* msg,uint64_t len);
-
-/**
- * @brief Send binary message to client with the set number.
- * @param num The client connection number.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return 1 if message send.
- */
-int ws_server_send_bin_client(int num,char* msg,uint64_t len);
-int ws_server_send_bin_client_from_callback(int num,char* msg,uint64_t len);
-
-/**
- * @brief Sends binary message to all clients with the set url.
- * @param url The clients url.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return The number of clients the message was send to.
- */
-int ws_server_send_bin_clients(char* url,char* msg,uint64_t len);
-
-/**
- * @brief Sends binary message to all clients.
- * @param msg The message to send.
- * @param len The length of the message to send.
- * @return The number of clients the message was send to.
- */
-int ws_server_send_bin_all(char* msg,uint64_t len);
-
-/**
- * @brief Websocket ping (not working yet)
- * @param num The connection number
- * @return 1 if succeeded.
- */
-int ws_server_ping(int num);
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
--- a/components/websocket/websocket.c	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,318 +0,0 @@
-/**
- * @file websocket.c
- * @brief Websocket functions.
- * @author Blake Felt - blake.w.felt@gmail.com`
- */
-
-#include "websocket.h"
-#include "lwip/tcp.h" // for the netconn structure
-#include "esp_system.h" // for esp_random
-#include "esp_log.h"
-#include "mbedtls/base64.h"
-#include "mbedtls/sha1.h"
-#include <string.h>
-
-static const char       *TAG = "websocket";
-
-ws_client_t ws_connect_client(struct netconn* conn,
-                              char* url,
-                              void (*ccallback)(WEBSOCKET_TYPE_t type,char* msg,uint64_t len),
-                              void (*scallback)(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len)
-                            ) {
-  ws_client_t client;
-  client.conn = conn;
-  client.connected = true;
-  client.url  = url;
-  client.ping = 0;
-  client.last_opcode = 0;
-  client.contin = NULL;
-  client.len = 0;
-  client.unfinished = 0;
-  client.ccallback = ccallback;
-  client.scallback = scallback;
-  return client;
-}
-
-void ws_disconnect_client(ws_client_t* client) {
-  ws_send(client,WEBSOCKET_OPCODE_CLOSE,NULL,0,1); // tell the client to close
-  if(client->conn) {
-    client->conn->callback = NULL; // shut off the callback
-    netconn_close(client->conn);
-    netconn_delete(client->conn);
-    client->conn = NULL;
-  }
-  client->connected = false;
-  client->url = NULL;
-  client->last_opcode = 0;
-  if(client->len) {
-    if(client->contin)
-      free(client->contin);
-    client->len = 0;
-  }
-  client->ccallback = NULL;
-  client->scallback = NULL;
-}
-
-bool ws_is_connected(ws_client_t client) {
-    if (client.conn && client.connected)
-	return 1;
-    return 0;
-}
-
-static void ws_generate_mask(ws_header_t* header) {
-  header->param.bit.MASK = 1;
-  header->key.full = esp_random(); // generate a random 32 bit number
-}
-
-static void ws_encrypt_decrypt(char* msg,ws_header_t header) {
-  if(header.param.bit.MASK) {
-    for(uint64_t i=0; i<header.length; i++) {
-      msg[i] ^= header.key.part[i%4];
-    }
-  }
-}
-
-
-
-err_t ws_send(ws_client_t* client,WEBSOCKET_OPCODES_t opcode,char* msg,uint64_t len,bool mask)
-{
-    static char		*out;
-    static char		*encrypt;
-    static uint64_t 	pos;
-    static uint64_t 	true_len;
-    ws_header_t		header;
-    static err_t	err;
-
-    header.param.pos.ZERO = 0; // reset the whole header
-    header.param.pos.ONE  = 0;
-
-    header.param.bit.FIN = 1; // all pieces are done (you don't need a huge message anyway...)
-    header.param.bit.OPCODE = opcode;
-    // populate LEN field
-    pos = 2;
-    header.length = len;
-    if (len<125) {
-    	header.param.bit.LEN = len;
-    } else if (len < 65536) {
-    	header.param.bit.LEN = 126;
-    	pos += 2;
-    } else {
-    	header.param.bit.LEN = 127;
-    	pos += 8;
-    }
-
-    if (mask) {
-    	ws_generate_mask(&header); // get a key
-    	encrypt = malloc(len); // allocate memory for the encryption
-    	memcpy(encrypt,msg,len);
-    	ws_encrypt_decrypt(encrypt,header); // encrypt it!
-    	pos += 4; // add the position
-    }
-
-    true_len = pos+len; // get the length of the entire message
-    pos = 2;
-    out = malloc(true_len); // allocate dat memory
-
-    if (out == NULL) {
-	ESP_LOGE(TAG, "ws_send malloc error for %llu bytes", true_len);
-	return ERR_MEM;
-    }
-
-    out[0] = header.param.pos.ZERO; // save header
-    out[1] = header.param.pos.ONE;
-
-    // put in the length, if necessary
-    if(header.param.bit.LEN == 126) {
-    	out[2] = (len >> 8) & 0xFF;
-    	out[3] = (len     ) & 0xFF;
-    	pos = 4;
-    }
-    if(header.param.bit.LEN == 127) {
-    	//memcpy(&out[2],&len,8);
-    	out[2] = (len >> 56) & 0xFF;
-    	out[3] = (len >> 48) & 0xFF;
-    	out[4] = (len >> 40) & 0xFF;
-    	out[5] = (len >> 32) & 0xFF;
-    	out[6] = (len >> 24) & 0xFF;
-    	out[7] = (len >> 16) & 0xFF;
-    	out[8] = (len >> 8)  & 0xFF;
-    	out[9] = (len)       & 0xFF;
-    	pos = 10;
-    }
-
-    if(mask) {
-    	out[pos] = header.key.part[0]; pos++;
-    	out[pos] = header.key.part[1]; pos++;
-    	out[pos] = header.key.part[2]; pos++;
-    	out[pos] = header.key.part[3]; pos++;
-    	memcpy(&out[pos],encrypt,len); // put in the encrypted message
-    	free(encrypt);
-    } else {
-    	memcpy(&out[pos],msg,len);
-    }
-
-    err = netconn_write(client->conn,out,true_len,NETCONN_COPY); // finally! send it.
-    if (err != ERR_OK) {
-	client->connected = false;
-	ESP_LOGE(TAG, "ws_send netconn_write error %d", err);
-    }
-    free(out); // free the entire message
-    return err;
-}
-
-
-
-char* ws_read(ws_client_t* client,ws_header_t* header) 
-{
-    char		*ret, *append;
-    err_t		err;
-    struct netbuf	*inbuf, *inbuf2;
-    char		*buf, *buf2;
-    uint16_t		len, len2;
-    uint64_t		pos, cont_len, cont_pos;
-
-    // if we read from this previously (not cont frames), stop reading
-    if(client->unfinished) {
-    	client->unfinished--;
-    	return NULL;
-    }
-
-    err = netconn_recv(client->conn,&inbuf);
-    if (err != ERR_OK) {
-	client->connected = false;
-	ESP_LOGE(TAG, "ws_read netconn_recv error %d", err);
-	return NULL;
-    }
-    netbuf_data(inbuf,(void**)&buf, &len);
-    if(!buf) return NULL;
-
-    // get the header
-    header->param.pos.ZERO = buf[0];
-    header->param.pos.ONE  = buf[1];
-
-  // get the message length
-    pos = 2;
-    if(header->param.bit.LEN < 125) {
-    	header->length = header->param.bit.LEN;
-    } else if(header->param.bit.LEN == 126) {
-    	header->length = buf[2] << 8 | buf[3];
-    	pos = 4;
-    } else { // LEN = 127
-    	header->length = (uint64_t)buf[2] << 56 | (uint64_t)buf[3] << 48
-                       | (uint64_t)buf[4] << 40 | (uint64_t)buf[5] << 32
-                       | (uint64_t)buf[6] << 24 | (uint64_t)buf[7] << 16
-                       | (uint64_t)buf[8] << 8  | (uint64_t)buf[9];
-    	pos = 10;
-    }
-
-    if(header->param.bit.MASK) {
-    	memcpy(&(header->key.full),&buf[pos],4); // extract the key
-    	pos += 4;
-    }
-
-    ret = malloc(header->length+1); // allocate memory, plus a byte
-    if(!ret) {
-	ESP_LOGE(TAG, "ws_read malloc error for %llu bytes", header->length+1);
-    	netbuf_delete(inbuf);
-    	header->received = 0;
-    	return NULL;
-    }
-
-    cont_len = len-pos; // get the actual length
-    memcpy(ret,&buf[pos],header->length); // allocate the total memory
-    cont_pos = cont_len; // get the initial position
-  
-    // netconn gives messages in pieces, so we need to get those (different than OPCODE_CONT)
-    while(cont_len < header->length) { // while the actual length is less than the header stated
-    	err = netconn_recv(client->conn,&inbuf2);
-    	if (err != ERR_OK) {
-	    ESP_LOGE(TAG, "ws_read netconn_recv error %d", err);
-      	    netbuf_delete(inbuf2);
-      	    free(ret);
-      	    client->unfinished = 0;
-      	    client->connected = false;
-      	    header->received = 0;
-      	    return NULL;
-    	}
-    
-	netbuf_data(inbuf2,(void**)&buf2, &len2);
-    	memcpy(&ret[cont_pos],buf2,len2);
-    	cont_pos += len2;
-    	if (!buf2) {
-      	    client->unfinished = 0;
-      	    header->received = 0;
-    	}
-    	netbuf_delete(inbuf2);
-    	client->unfinished++;
-    	cont_len += len2;
-    }
-
-    ret[header->length] = '\0'; // end string
-    ws_encrypt_decrypt(ret,*header); // unencrypt, if necessary
-
-  if(header->param.bit.FIN == 0) { // if the message isn't done
-    if((header->param.bit.OPCODE == WEBSOCKET_OPCODE_CONT) &&
-       ((client->last_opcode==WEBSOCKET_OPCODE_BIN) || (client->last_opcode==WEBSOCKET_OPCODE_TEXT))) {
-         cont_len = header->length + client->len;
-         append = malloc(cont_len);
-         memcpy(append,client->contin,client->len);
-         memcpy(&append[client->len],ret,header->length);
-         free(client->contin);
-         client->contin = malloc(cont_len);
-         client->len = cont_len;
-
-         free(append);
-         free(ret);
-         netbuf_delete(inbuf);
-         //free(buf);
-         return NULL;
-    }
-    else if((header->param.bit.OPCODE==WEBSOCKET_OPCODE_BIN) || (header->param.bit.OPCODE==WEBSOCKET_OPCODE_TEXT)) {
-      if(client->len) {
-        free(client->contin);
-      }
-      client->contin = malloc(header->length);
-      memcpy(client->contin,ret,header->length);
-      client->len = header->length;
-      client->last_opcode = header->param.bit.OPCODE;
-
-      free(ret);
-      netbuf_delete(inbuf);
-      //free(buf);
-      return NULL;
-    }
-    else { // there shouldn't be another FIN code....
-      free(ret);
-      netbuf_delete(inbuf);
-      //free(buf);
-      return NULL;
-    }
-  }
-  client->last_opcode = header->param.bit.OPCODE;
-  if(inbuf) netbuf_delete(inbuf);
-  header->received = 1;
-  return ret;
-}
-
-char* ws_hash_handshake(char* handshake,uint8_t len) {
-  const char hash[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-  const uint8_t hash_len = sizeof(hash);
-  char* ret;
-  char key[64];
-  unsigned char sha1sum[20];
-  unsigned int ret_len;
-
-  if(!len) return NULL;
-  ret = malloc(32);
-
-  memcpy(key,handshake,len);
-  strlcpy(&key[len],hash,sizeof(key));
-  mbedtls_sha1((unsigned char*)key,len+hash_len-1,sha1sum);
-  mbedtls_base64_encode(NULL, 0, &ret_len, sha1sum, 20);
-  if(!mbedtls_base64_encode((unsigned char*)ret,32,&ret_len,sha1sum,20)) {
-    ret[ret_len] = '\0';
-    return ret;
-  }
-  free(ret);
-  return NULL;
-}
--- a/components/websocket/websocket_server.c	Mon Jul 01 08:38:57 2024 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,489 +0,0 @@
-/**
- * @file websocket_server.c
- * @brief Websocket server functions.
- * @author Blake Felt - blake.w.felt@gmail.com`
- */
-
-#include "websocket_server.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/semphr.h"
-#include "freertos/task.h"
-#include "freertos/queue.h"
-#include "lwip/tcp.h"
-#include <string.h>
-
-static SemaphoreHandle_t xwebsocket_mutex; // to lock the client array
-static QueueHandle_t xwebsocket_queue; // to hold the clients that send messages
-static ws_client_t clients[WEBSOCKET_SERVER_MAX_CLIENTS]; // holds list of clients
-static TaskHandle_t xtask; // the task itself
-
-/**
- * @brief Add a connection to the callback queue.
- * @param conn The network connection.
- * @param evt The event.
- * @param len Not used.
- */
-static void background_callback(struct netconn* conn, enum netconn_evt evt,u16_t len) {
-  switch(evt) {
-    case NETCONN_EVT_RCVPLUS:
-      xQueueSendToBack(xwebsocket_queue,&conn,WEBSOCKET_SERVER_QUEUE_TIMEOUT);
-      break;
-    default:
-      break;
-  }
-}
-
-/**
- * @brief Handle client read.
- * @param num The client connection number.
- */
-static void handle_read(uint8_t num) 
-{
-    ws_header_t header;
-    char* msg;
-
-    header.received = 0;
-    msg = ws_read(&clients[num],&header);
-
-    if (!header.received) {
-    	if (msg) 
-	    free(msg);
-    	return;
-    }
-
-    switch(clients[num].last_opcode) {
-    	case WEBSOCKET_OPCODE_CONT:
-      		break;
-    	case WEBSOCKET_OPCODE_BIN:
-      		clients[num].scallback(num,WEBSOCKET_BIN,msg,header.length);
-      		break;
-    	case WEBSOCKET_OPCODE_TEXT:
-      		clients[num].scallback(num,WEBSOCKET_TEXT,msg,header.length);
-      		break;
-    	case WEBSOCKET_OPCODE_PING:
-      		ws_send(&clients[num],WEBSOCKET_OPCODE_PONG,msg,header.length,0);
-      		clients[num].scallback(num,WEBSOCKET_PING,msg,header.length);
-      		break;
-    	case WEBSOCKET_OPCODE_PONG:
-      		if (clients[num].ping) {
-        	    clients[num].scallback(num,WEBSOCKET_PONG,NULL,0);
-        	    clients[num].ping = 0;
-      		}
-      		break;
-    	case WEBSOCKET_OPCODE_CLOSE:
-      		clients[num].scallback(num,WEBSOCKET_DISCONNECT_EXTERNAL,NULL,0);
-      		ws_disconnect_client(&clients[num]);
-      		break;
-    	default:
-      		break;
-    }
-
-    if(msg) 
-	free(msg);
-}
-
-/**
- * @brief The webserver task.
- */
-static void ws_server_task(void* pvParameters) 
-{
-    struct netconn* conn;
-
-    xwebsocket_mutex = xSemaphoreCreateMutex();
-    xwebsocket_queue = xQueueCreate(WEBSOCKET_SERVER_QUEUE_SIZE, sizeof(struct netconn*));
-
-    // initialize all clients
-    for (int i = 0; i < WEBSOCKET_SERVER_MAX_CLIENTS; i++) {
-    	clients[i].conn = NULL;
-	clients[i].connected = false;
-    	clients[i].url  = NULL;
-    	clients[i].ping = 0;
-    	clients[i].last_opcode = 0;
-    	clients[i].contin = NULL;
-    	clients[i].len = 0;
-    	clients[i].ccallback = NULL;
-    	clients[i].scallback = NULL;
-    }
-
-    for(;;) {
-    	xQueueReceive(xwebsocket_queue, &conn, portMAX_DELAY);
-    	if(!conn) continue; // if the connection was NULL, ignore it
-
-    	if (xSemaphoreTake(xwebsocket_mutex, 25) == pdTRUE) { // take access
-    	    for(int i = 0; i < WEBSOCKET_SERVER_MAX_CLIENTS; i++) {
-      	    	if(clients[i].conn == conn) {
-        	    handle_read(i);
-        	    break;
-      	    	}
-    	    }
-    	    xSemaphoreGive(xwebsocket_mutex); // return access
-    	}
-    }
-    vTaskDelete(NULL);
-}
-
-
-
-int ws_server_start() 
-{
-    if(xtask)
-	return 0;
-  
-    xTaskCreate(&ws_server_task, "ws_server_task", WEBSOCKET_SERVER_TASK_STACK_DEPTH, NULL, WEBSOCKET_SERVER_TASK_PRIORITY, &xtask);
-    return 1;
-}
-
-
-
-int ws_server_stop() {
-    if(!xtask) 
-	return 0;
-  
-    vTaskDelete(xtask);
-    return 1;
-}
-
-
-
-/**
- * @brief Prepare a response for a new websocket connection.
- * @param buf The client message buffer.
- * @param buflen The length of the buffer.
- * @param handshake The jandshake hash.
- * @param protocol The requested protocol or NULL.
- * @return True iif success, else false.
- */
-static bool prepare_response(char* buf,uint32_t buflen,char* handshake,char* protocol) {
-  const char WS_HEADER[] = "Upgrade: websocket\r\n";
-  const char WS_KEY[] = "Sec-WebSocket-Key: ";
-  const char WS_RSP[] = "HTTP/1.1 101 Switching Protocols\r\n" \
-                        "Upgrade: websocket\r\n" \
-                        "Connection: Upgrade\r\n" \
-                        "Sec-WebSocket-Accept: %s\r\n" \
-                        "%s\r\n";
-
-  char* key_start;
-  char* key_end;
-  char* hashed_key;
-
-  if(!strstr(buf,WS_HEADER)) return 0;
-  if(!buflen) return 0;
-  key_start = strstr(buf,WS_KEY);
-  if(!key_start) return 0;
-  key_start += 19;
-  key_end = strstr(key_start,"\r\n");
-  if(!key_end) return 0;
-
-  hashed_key = ws_hash_handshake(key_start,key_end-key_start);
-  if(!hashed_key) return 0;
-  if(protocol) {
-    char tmp[256];
-    sprintf(tmp,WS_RSP,hashed_key,"Sec-WebSocket-Protocol: %s\r\n");
-    sprintf(handshake,tmp,protocol);
-  }
-  else {
-    sprintf(handshake,WS_RSP,hashed_key,"");
-  }
-  free(hashed_key);
-  return 1;
-}
-
-int ws_server_add_client_protocol(struct netconn* conn,
-                         char* msg,
-                         uint16_t len,
-                         char* url,
-                         char* protocol,
-                         void (*callback)(uint8_t num,
-                                          WEBSOCKET_TYPE_t type,
-                                          char* msg,
-                                          uint64_t len)) {
-  int ret;
-  char handshake[256];
-
-    if(!prepare_response(msg,len,handshake,protocol)) {
-    	netconn_close(conn);
-    	netconn_delete(conn);
-    	return -2;
-    }
-
-    ret = -1;
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	conn->callback = background_callback;
-  	netconn_write(conn,handshake,strlen(handshake),NETCONN_COPY);
-
-    	for(int i = 0; i < WEBSOCKET_SERVER_MAX_CLIENTS; i++) {
-    	    if(clients[i].conn) continue;
-    	    callback(i,WEBSOCKET_CONNECT,NULL,0);
-    	    clients[i] = ws_connect_client(conn,url,NULL,callback);
-    	    if(!ws_is_connected(clients[i])) {
-      	    	callback(i,WEBSOCKET_DISCONNECT_ERROR,NULL,0);
-      	    	ws_disconnect_client(&clients[i]);
-      	    	break;
-    	    }
-    	    ret = i;
-    	    break;
-    	}
-    	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-int ws_server_len_url(char* url) 
-{
-    int ret;
-    ret = 0;
-  
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	for(int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	    if(clients[i].url && !strcmp(url,clients[i].url)) 
-		ret++;
-  	}
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-int ws_server_add_client(struct netconn* conn,
-                         char* msg,
-                         uint16_t len,
-                         char* url,
-                         void (*callback)(uint8_t num,
-                                          WEBSOCKET_TYPE_t type,
-                                          char* msg,
-                                          uint64_t len)) {
-
-  return ws_server_add_client_protocol(conn,msg,len,url,NULL,callback);
-
-}
-
-int ws_server_len_all() 
-{
-    int ret;
-    ret = 0;
-  
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	for(int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	    if(clients[i].conn) 
-		ret++;
-  	}
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-int ws_server_remove_client(int num) 
-{
-    int ret = 0;
-  
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	if(ws_is_connected(clients[num])) {
-    	    clients[num].scallback(num,WEBSOCKET_DISCONNECT_INTERNAL,NULL,0);
-    	    ws_disconnect_client(&clients[num]);
-    	    ret = 1;
-  	}
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-int ws_server_remove_clients(char* url) 
-{
-    int ret = 0;
-  
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	for(int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	    if(ws_is_connected(clients[i]) && !strcmp(url,clients[i].url)) {
-      		clients[i].scallback(i,WEBSOCKET_DISCONNECT_INTERNAL,NULL,0);
-      		ws_disconnect_client(&clients[i]);
-      		ret += 1;
-    	    }
-  	}
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-int ws_server_remove_all() 
-{
-    int ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	for(int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	    if(ws_is_connected(clients[i])) {
-      		clients[i].scallback(i,WEBSOCKET_DISCONNECT_INTERNAL,NULL,0);
-      		ws_disconnect_client(&clients[i]);
-      		ret += 1;
-    	    }
-  	}
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-// The following functions are already written below, but without the mutex.
-
-int ws_server_send_text_client(int num,char* msg,uint64_t len)
-{
-    int ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	ret = ws_server_send_text_client_from_callback(num, msg, len);
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-int ws_server_send_text_clients(char* url,char* msg,uint64_t len) 
-{
-    int ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	ret = ws_server_send_text_clients_from_callback(url, msg, len);
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-int ws_server_send_text_all(char* msg,uint64_t len) 
-{
-    int ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-  	ret = ws_server_send_text_all_from_callback(msg, len);
-  	xSemaphoreGive(xwebsocket_mutex);
-    }
-  
-    return ret;
-}
-
-
-
-// the following functions should be used inside of the callback. The regular versions
-// grab the mutex, but it is already grabbed from inside the callback so it will hang.
-
-int ws_server_send_text_client_from_callback(int num,char* msg,uint64_t len) 
-{
-    int ret = 0;
-  
-    if (ws_is_connected(clients[num])) {
-    	if ((ws_send(&clients[num],WEBSOCKET_OPCODE_TEXT,msg,len,0) == ERR_OK)) {
-    	    ret = 1;
-	} else {
-      	    clients[num].scallback(num,WEBSOCKET_DISCONNECT_ERROR,NULL,0);
-            ws_disconnect_client(&clients[num]);
-      	    ret = 0;
-    	}
-    }
-    return ret;
-}
-
-
-
-int ws_server_send_text_clients_from_callback(char* url,char* msg,uint64_t len) 
-{
-    int ret = 0;
-    
-    for (int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	if (ws_is_connected(clients[i]) && !strcmp(clients[i].url,url)) {
-      	    if ((ws_send(&clients[i],WEBSOCKET_OPCODE_TEXT,msg,len,0) == ERR_OK)) {
-		ret++;
-	    } else {
-        	clients[i].scallback(i,WEBSOCKET_DISCONNECT_ERROR,NULL,0);
-        	ws_disconnect_client(&clients[i]);
-      	    }
-    	}
-    }
-    return ret;
-}
-
-
-
-int ws_server_send_text_all_from_callback(char* msg,uint64_t len) 
-{
-    int ret = 0;
-  
-    for (int i=0;i<WEBSOCKET_SERVER_MAX_CLIENTS;i++) {
-    	if (ws_is_connected(clients[i])) {
-      	    if ((ws_send(&clients[i],WEBSOCKET_OPCODE_TEXT,msg,len,0) == ERR_OK)) {
-		ret++;
-	    } else {
-        	clients[i].scallback(i,WEBSOCKET_DISCONNECT_ERROR,NULL,0);
-        	ws_disconnect_client(&clients[i]);
-      	    }
-    	}
-    }
-    return ret;
-}
-
-
-
-int ws_server_send_bin_client_from_callback(int num,char* msg,uint64_t len)
-{
-    int ret = 0;
-
-    if (ws_is_connected(clients[num])) {
-        if ((ws_send(&clients[num],WEBSOCKET_OPCODE_BIN,msg,len,0) == ERR_OK)) {
-	    ret = 1;
-	} else {
-	    clients[num].scallback(num,WEBSOCKET_DISCONNECT_ERROR,NULL,0);
-	    ws_disconnect_client(&clients[num]);
-	    ret = 0;
-	}
-    }
-
-    return ret;
-}
-
-
-
-int ws_server_send_bin_client(int num,char* msg,uint64_t len) 
-{
-    int	ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-    	ret = ws_server_send_bin_client_from_callback(num, msg, len);
-    	xSemaphoreGive(xwebsocket_mutex);
-    }
-    return ret;
-}
-
-
-
-int ws_server_ping(int num) 
-{
-    int ret = 0;
-
-    if (xSemaphoreTake(xwebsocket_mutex, 10) == pdTRUE) {
-    	if (ws_is_connected(clients[num])) {
-	    if ((ws_send(&clients[num], WEBSOCKET_OPCODE_PING, (char *)"BrewPing", 8, 0) == ERR_OK)) {
-	    	ret = 1;
-	    } else {
-	    	clients[num].scallback(num, WEBSOCKET_DISCONNECT_ERROR, NULL, 0);
-	    	ws_disconnect_client(&clients[num]);
-	    	ret = 0;
-	    }
-    	}
-    	xSemaphoreGive(xwebsocket_mutex);
-    }
-    
-    return ret;
-}
-
--- a/image/version.txt	Mon Jul 01 08:38:57 2024 +0200
+++ b/image/version.txt	Wed Jul 03 20:01:31 2024 +0200
@@ -1,1 +1,1 @@
-0.3.18
+0.3.19
--- a/image/w/webui.html	Mon Jul 01 08:38:57 2024 +0200
+++ b/image/w/webui.html	Wed Jul 03 20:01:31 2024 +0200
@@ -110,10 +110,10 @@
 
   <div id="row_info">
    <div style="color: yellow; text-align: center;">
-    <p />Written by Michiel Broek &copy; 2018-2021
+    <p />Written by Michiel Broek &copy; 2018-2024
    </div>
    <div style="color: orange; text-align: center;">
-    <p />Parts are written by Chris Morgan, Brett Beauregard, Blake Felt, LoBo and David Antliff.
+    <p />Parts are written by Chris Morgan, Brett Beauregard, LoBo and David Antliff.
    </div>
    <div>
     <button type="button" class="okbutton" id="ok_info">Ok</button>
--- a/main/automation.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/automation.c	Wed Jul 03 20:01:31 2024 +0200
@@ -243,7 +243,7 @@
                 }
                 MashState = Sub_Screen = MASH_NONE;
 		snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-		ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+		ws_server_send_text_clients(msg);
                 pumpTime = 0;
                 pumpRest = false;
 		runtime.StageResume = Main_Screen;
@@ -454,7 +454,7 @@
 		read_recipe(config.RecipeRec);
 		snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\",\"brew1\":\"%s\",\"brew2\":\"%s\"}", 
 			Main_Screen, Sub_Screen, equipment.Name, recipe.Name);
-		ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+		ws_server_send_text_clients(msg);
 		y = 28;
 		TopMessage((char *)"Automaat");
 		TFT_setFont(DEFAULT_FONT, NULL);
@@ -674,7 +674,7 @@
                     }
                     MashState = Sub_Screen = MASH_WAITTEMP;
 		    snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                    ws_server_send_text_clients(msg);
                     log_msg(TAG, "Mash step %d type: %s time: %d sp: %6.4f-%6.4f sv: %6.4f",
 				    runtime.MashStep, mashTypes[recipe.MashStep[runtime.MashStep].Type],
                                     stageTime, recipe.MashStep[runtime.MashStep].Step_temp, recipe.MashStep[runtime.MashStep].End_temp, temp_MLT);
@@ -720,7 +720,7 @@
 			TFT_print(temp_buf, CENTER, 135);
 			SoundPlay(SOUND_Prompt);
 			snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"%s\"}", Main_Screen, Sub_Screen, temp_buf);
-                	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                	ws_server_send_text_clients(msg);
 			if (recipe.MashStep[runtime.MashStep].Type == MASHTYPE_INFUSION) {
 			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
 			    	// No heating during the infusion.
@@ -746,7 +746,7 @@
 			newTemp = stageTemp;
                         MashState = Sub_Screen = MASH_REST;
 			snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                	ws_server_send_text_clients(msg);
                         if (! runtime.MaltAdded && runtime.MashStep == 0) {
                             TimerSet(0);
                         } else {
@@ -839,7 +839,7 @@
 			updateRuntime = true;
 			TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
 			snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                        ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                        ws_server_send_text_clients(msg);
 
                         if (runtime.MashStep == 0 && ! runtime.MaltAdded && config.AskAdd) {
                             /*
@@ -863,7 +863,7 @@
                             SoundPlay(SOUND_Prompt);
 			    MashState = Sub_Screen = MASH_ADD;
 			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Mout storten?\"}", Main_Screen, Sub_Screen);
-                	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                	    ws_server_send_text_clients(msg);
 			    log_msg(TAG, "Mash add prompt");
 			    break;
                         }
@@ -886,7 +886,7 @@
                             TimerSet(config.IodineTime * 60);
 			    MashState = Sub_Screen = MASH_IODINE;
 			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Jodium test?\"}", Main_Screen, Sub_Screen);
-                	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                	    ws_server_send_text_clients(msg);
 			    log_msg(TAG, "Mash iodine test prompt");
 			    break;
                         }
@@ -907,7 +907,7 @@
                             SoundPlay(SOUND_Prompt);
 			    MashState = Sub_Screen = MASH_REMOVE;
 			    snprintf(msg, 127, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Mout verwijderen?\"}", Main_Screen, Sub_Screen);
-                	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                	    ws_server_send_text_clients(msg);
 			    log_msg(TAG, "Mash remove prompt");
 			    break;
                         }
@@ -1008,7 +1008,7 @@
 				    }
 				    MashState = Sub_Screen = MASH_WAITTEMP;
 				    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                                    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                                    ws_server_send_text_clients(msg);
 				    break;
 			default:    break;
 		    }
@@ -1179,7 +1179,7 @@
 			    stageTemp = recipe.CoolTemp;
 			}
 			snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                        ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                        ws_server_send_text_clients(msg);
 			CoolBeep = false;
 			log_msg(TAG, "Start cooling from %6.2f to %4.1f", ds18b20_state->mlt_temperature, stageTemp);
 			if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
@@ -1232,7 +1232,7 @@
                     	xSemaphoreGive(xSemaphoreDriver);
 			if (Sub_Screen == 2) {
 			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                            ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                            ws_server_send_text_clients(msg);
 			}
                     }
                     switch (Buttons_Scan()) {
@@ -1345,7 +1345,7 @@
 			TFT_fillScreen(_bg);
 			Sub_Screen = 1;
                         snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                        ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+                        ws_server_send_text_clients(msg);
 			if (Main_Screen == MAIN_AUTO_WHIRLPOOL9) {
 			    TimeWhirlPool = recipe.Whirlpool9;
 			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
--- a/main/brewboard.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/brewboard.c	Wed Jul 03 20:01:31 2024 +0200
@@ -168,7 +168,7 @@
     TFT_print((char *)" Ok\r\n", LASTX, LASTY);
     SoundPlay(SOUND_StartUp);
 
-    start_http_websocket();
+    install_http_server();
     vTaskDelay(1000 / portTICK_PERIOD_MS);
     Main_Screen = MAIN_MODE_FREE;
 
--- a/main/config.h	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/config.h	Wed Jul 03 20:01:31 2024 +0200
@@ -36,6 +36,7 @@
 #include "esp_netif.h"
 #include "esp_mac.h"
 #include "esp_wifi.h"
+#include "esp_http_server.h"
 
 #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
 #include "esp_wpa2.h"
@@ -62,7 +63,6 @@
 #include "tftspi.h"
 #include "tft.h"
 #include "PID_v1.h"
-#include "websocket_server.h"
 
 #include "buttons.h"
 #include "calibration.h"
--- a/main/task_ds18b20.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/task_ds18b20.c	Wed Jul 03 20:01:31 2024 +0200
@@ -27,6 +27,7 @@
 float               	Fake_MLT = 18.90;
 float			Fake_HLT = 18.70;
 extern double		Output;
+extern my_equipment_t	equipment;
 #endif
 
 static const char	*TAG = "task_ds18b20";
--- a/main/task_http.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/task_http.c	Wed Jul 03 20:01:31 2024 +0200
@@ -1,23 +1,21 @@
 /**
  * @file task_http.c
  * @brief HTTP and Websocket server functions.
- *        This uses some ESP32 Websocket code written by Blake Felt - blake.w.felt@gmail.com
  */
 #include "config.h"
+#include "sys/param.h"
 #include "mbedtls/base64.h"
 #include "mbedtls/sha1.h"
 #include "cJSON.h"
 
 
 static const char		*TAG = "task_http";
-static QueueHandle_t		client_queue;
-const static int		client_queue_size = 10;
-static TaskHandle_t             xTaskHTTP  = NULL;
-static TaskHandle_t             xTaskQueue = NULL;
+httpd_handle_t			web_server = NULL;			///< The http server handle.
 
 cJSON				*root = NULL;
 cJSON				*touch = NULL;
 
+
 typedef struct _ls_list {
     struct _ls_list		*next;
     char			d_name[64];
@@ -26,7 +24,9 @@
 } ls_list;
 
 
-
+/**
+ * @brief Clear the linked list and release memory.
+ */
 void tidy_lslist(ls_list **lap)
 {
     ls_list	*tmp, *old;
@@ -39,7 +39,13 @@
 }
 
 
-
+/**
+ * @brief Add a file entry to the linked list.
+ * @param lap Pointer to the linked list.
+ * @param name The entry filename.
+ * @param size The entry filesize.
+ * @param mtime The entry filedate.
+ */
 void fill_list(ls_list **lap, char *name, off_t size, long mtime)
 {
     ls_list	**tmp;
@@ -55,7 +61,11 @@
 }
 
 
-
+/**
+ * @brief Compare for sorting files by datetime, reversed.
+ * @param lsp1 Entry 1
+ * @param lsp2 Entry 2
+ */
 int comp(ls_list **lsp1, ls_list **lsp2)
 {
     char	as[20], bs[20];
@@ -67,7 +77,9 @@
 }
 
 
-
+/**
+ * @brief Sort the linked list.
+ */
 void sort_list(ls_list **lap)
 {
     ls_list	*ta, **vector;
@@ -101,74 +113,199 @@
 }
 
 
+#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
 
 /**
- * @brief Debug dump buffer
- * @param buf The buffer
- * @param buflen Length of the buffer
+ * @brief Set HTTP response content type according to file extension
+ * @param req The request where the content type will be set.
+ * @param filepath The filename to get the extension from.
+ * @return ESP_OK if success.
  */
-#if 0
-void dump_buf(char *buf, uint16_t buflen);
-#endif
+static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath)
+{
+    const char *type = "text/plain";
+    if (CHECK_FILE_EXTENSION(filepath, ".html")) {
+        type = "text/html";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".log")) {
+	type = "text/plain";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".js")) {
+        type = "application/javascript";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".json")) {
+	type = "text/json";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".gz")) {
+	type = "application/x-gzip";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".css")) {
+        type = "text/css";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".png")) {
+        type = "image/png";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".ico")) {
+        type = "image/x-icon";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".svg")) {
+        type = "text/xml";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".ttf")) {
+	type = "font/ttf";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".woff")) {
+	type = "font/woff";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".woff2")) {
+        type = "font/woff2";
+    } else if (CHECK_FILE_EXTENSION(filepath, ".xml")) {
+	type = "text/xml";
+    }
+    return httpd_resp_set_type(req, type);
+}
 
 
 /**
- * @brief Send HTTP error and message
- * @param conn The socket to send to.
- * @param error The HTTP error code.
- * @param message Yhe human readable explanation.
- * @paeam body The human readable explanation.
+ * @brief Send text message to a single websocket client.
+ * @param num The client socket to send to.
+ * @param msg The message to send.
+ * @return ESP_OK.
  */
-static void http_error(struct netconn *conn, int error, char *message, char *body)
+int ws_server_send_text_client(int num, char* msg)
 {
-    char	*tmp = malloc(200);
+    httpd_ws_frame_t    ws_pkt;
+
+    memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
+    ws_pkt.type = HTTPD_WS_TYPE_TEXT;
+    ws_pkt.len = strlen(msg);
+    ws_pkt.payload = (uint8_t*)msg;
+    httpd_ws_send_frame_async(web_server, num, &ws_pkt);
+    ESP_LOGD(TAG, "ws_server_send_text_client(%d, %s)", num, msg);
+    return ESP_OK;
+}
+
 
-    ESP_LOGI(TAG, "Http response %d - %s", error, message);
-    if (strlen(body)) {
-	snprintf(tmp, 199, "HTTP/1.1 %d %s\r\nContent-type: text/plain\r\n\r\n%s\n", error, message, body);
-    } else {
-        snprintf(tmp, 199, "HTTP/1.1 %d %s\r\n\r\n", error, message);
+/**
+ * @brief Broadcast text message to all connected websocket clients.
+ * @param msg The text message.
+ * @return -1 if error, else the number of clients.
+ */
+int ws_server_send_text_clients(char* msg)
+{
+    httpd_ws_frame_t	ws_pkt;
+    static size_t	max_clients = CONFIG_LWIP_MAX_LISTENING_TCP;
+    size_t		fds = max_clients;
+    int			client_fds[CONFIG_LWIP_MAX_LISTENING_TCP] = {0};
+    int			count = 0;
+
+    memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
+    ws_pkt.type = HTTPD_WS_TYPE_TEXT;
+    ws_pkt.len = strlen(msg);
+    ws_pkt.payload = (uint8_t*)msg;
+
+    esp_err_t ret = httpd_get_client_list(web_server, &fds, client_fds);
+    if (ret != ESP_OK) {
+        return -1;
     }
-    netconn_write(conn, tmp, strlen(tmp), NETCONN_NOCOPY);
-    free(tmp);
+
+    for (int i = 0; i < fds; i++) {
+        httpd_ws_client_info_t client_info = httpd_ws_get_fd_info(web_server, client_fds[i]);
+        if (client_info == HTTPD_WS_CLIENT_WEBSOCKET) {
+            httpd_ws_send_frame_async(web_server, client_fds[i], &ws_pkt);
+            count++;
+        }
+    }
+    return count;
 }
 
 
+/**
+ * @brief Websocket handler.
+ * @param req The request structure.
+ * @return ESP_OK or error.
+ */
+static esp_err_t ws_handler(httpd_req_t *req)
+{
+    if (req->method == HTTP_GET) {
+        ESP_LOGI(TAG, "New websocket connection %d", httpd_req_to_sockfd(req));
+	/* Send initial screen */
+	TFTstartWS(httpd_req_to_sockfd(req));
+        return ESP_OK;
+    }
+
+    httpd_ws_frame_t	ws_pkt;
+    uint8_t		*buf = NULL;
+    char		jbuf[128];
+    cJSON		*root = NULL, *touch = NULL;
+
+    memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
+    ws_pkt.type = HTTPD_WS_TYPE_TEXT;
+    /* Set max_len = 0 to get the frame len */
+    esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "ws_handler() httpd_ws_recv_frame failed to get frame len with %d", ret);
+        return ret;
+    }
+    ESP_LOGD(TAG, "frame len is %d type is %d socket %d", ws_pkt.len, ws_pkt.type, httpd_req_to_sockfd(req));
+
+    if (ws_pkt.len) {
+        /* ws_pkt.len + 1 is for NULL termination as we are expecting a string */
+        buf = calloc(1, ws_pkt.len + 1);
+        if (buf == NULL) {
+            ESP_LOGE(TAG, "ws_handler() no memory for buf");
+            return ESP_ERR_NO_MEM;
+        }
+        ws_pkt.payload = buf;
+        /* Set max_len = ws_pkt.len to get the frame payload */
+        ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG, "ws_handler() httpd_ws_recv_frame failed with %d", ret);
+            free(buf);
+            return ret;
+        }
+        ESP_LOGI(TAG, "ws_handler() Got message: %s", ws_pkt.payload);
+    }
+
+    if ((ws_pkt.type == HTTPD_WS_TYPE_TEXT) && (ws_pkt.len > 0) && (ws_pkt.len < 128)) {
+        memcpy(jbuf, ws_pkt.payload, ws_pkt.len);
+        jbuf[ws_pkt.len] = '\0';
+        if ((root = cJSON_Parse(jbuf))) {
+            if ((touch = cJSON_GetObjectItem(root,"touch"))) {
+                int x = cJSON_GetObjectItem(touch, "x")->valueint;
+                int y = cJSON_GetObjectItem(touch, "y")->valueint;
+		WS_touched(x, y);
+            } else {
+                ESP_LOGI(TAG,"not json touch");
+            }
+            cJSON_Delete(root);
+        } else {
+            ESP_LOGI(TAG,"not json");
+        }
+    }
+
+    free(buf);
+    return ESP_OK;
+}
+
 
 /**
- * @brief Send HTTP file from the filesystem.
- * @param conn The network connection.
- * @param url The requested url.
- * @param mod_since The date/time of the file, or NULL.
- * @param ipstr The IP address of the remote.
+ * @brief Handle files download from /spiffs or /sdcard.
  */
-static void http_sendfile(struct netconn *conn, char *url, char *mod_since, char *ipstr)
+static esp_err_t files_handler(httpd_req_t *req)
 {
-    char	temp_url[128], temp_url_gz[132], header[256], c_type[32];
+    char	temp_url[560], temp_url_gz[564];
     struct stat	st;
     off_t	filesize;
-    size_t	sentsize;
     char	strftime_buf[64];
-    err_t	err;
     bool	send_gz;
-    FILE	*f;
+    int		fd = -1;
 
-    if (url[strlen(url) - 1] == '/') {
-	// If no filename given, use index.html
-	sprintf(temp_url, "/spiffs/w%sindex.html", url);
-    } else if (strncmp(url, "/log/", 5) == 0) {
-	// Logfiles are on the SD card.
-	sprintf(temp_url, "/sdcard/w%s", url);
+    if (req->uri[strlen(req->uri) - 1] == '/') {
+        // If no filename given, use index.html
+        sprintf(temp_url, "/spiffs/w%sindex.html", req->uri);
+    } else if (strncmp(req->uri, "/log/", 5) == 0) {
+        // Logfiles are on the SD card.
+        sprintf(temp_url, "/sdcard/w%s", req->uri);
     } else {
-	sprintf(temp_url, "/spiffs/w%s", url);
-	for (int i = 0; i < 127; i++) {
-	    if (temp_url[i] == '?')
-		temp_url[i] = '\0';
-	    if (temp_url[i] == '\0')
-		break;
-	}
+        sprintf(temp_url, "/spiffs/w%s", req->uri);
+        for (int i = 0; i < 127; i++) {
+            if (temp_url[i] == '?')
+                temp_url[i] = '\0';
+            if (temp_url[i] == '\0')
+                break;
+        }
     }
-    snprintf(temp_url_gz, 131, "%s.gz", temp_url);
+    snprintf(temp_url_gz, 563, "%s.gz", temp_url);
 
     /*
      * Get filesize and date for the response headers.
@@ -180,6 +317,7 @@
     strftime_buf[0] = '\0';
     send_gz = false;
     if (stat(temp_url_gz, &st) == 0) {
+	/* The requested file is gzipped. */
 	filesize = st.st_size;
 	strftime(strftime_buf, sizeof(strftime_buf), "%a, %d %b %Y %T %z", localtime(&(st.st_mtime)));
 	send_gz = true;
@@ -187,428 +325,201 @@
 	filesize = st.st_size;
 	strftime(strftime_buf, sizeof(strftime_buf), "%a, %d %b %Y %T %z", localtime(&(st.st_mtime)));
     }
-
-    /*
-     * If we have a If-Modified-Since parameter, compare that with the current
-     * filedate on disk. If It's the same send a 304 response.
-     * Cannot work on /spiffs.
-     * Update 18-05-2021 it seems better on idf 4.2.1 but still not correct.
-     */
-#if 0
-    time_t    Now;
-    struct tm timeInfo;
-    if (mod_since && strlen(strftime_buf)) {
-	time(&Now);
-	localtime_r(&Now, &timeInfo);
-	strftime(strftime_buf, sizeof(strftime_buf), "%a, %d %b %Y %T %z", &timeInfo);
-	sprintf(header, "HTTP/1.1 304 Not Modified\r\nDate: %s\r\n\r\n", strftime_buf);
-	netconn_write(conn, header, strlen(header), NETCONN_NOCOPY);
-	ESP_LOGI(TAG, "%s sendfile %s Not Modified, ok", ipstr, temp_url);
-	return;
-    }
-#endif
+    ESP_LOGI(TAG, "GET `%s` file %s date %s size %d", req->uri, (send_gz) ? temp_url_gz : temp_url, strftime_buf, (int)filesize);
 
     if (send_gz) {
-	f = fopen(temp_url_gz, "r");
+	fd = open(temp_url_gz, O_RDONLY, 0);
     } else {
-    	f = fopen(temp_url, "r");
+	fd = open(temp_url, O_RDONLY, 0);
     }
-    if (f == NULL) {
-	ESP_LOGI(TAG, "%s url \'%s\' file \'%s\' not found", ipstr, url, temp_url);
-	http_error(conn, 404, (char *)"Not found", (char *)"Not found");
-	return;
+    if (fd == -1) {
+	ESP_LOGI(TAG, "files_handler() file not found");
+	httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
+	return ESP_FAIL;
+    }
+
+    set_content_type_from_file(req, temp_url);
+    if (send_gz) {
+	httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
     }
 
-    if (strcmp(".html", &temp_url[strlen(temp_url) - 5]) == 0) {
-	sprintf(c_type, "text/html");
-    } else if (strcmp(".css", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "text/css");
-    } else if (strcmp(".log", &temp_url[strlen(temp_url) - 4]) == 0) {
-        sprintf(c_type, "text/plain");
-    } else if (strcmp(".js", &temp_url[strlen(temp_url) - 3]) == 0) {
-	sprintf(c_type, "text/javascript");
-    } else if (strcmp(".json", &temp_url[strlen(temp_url) - 5]) == 0) {
-	sprintf(c_type, "text/json");
-    } else if (strcmp(".gz", &temp_url[strlen(temp_url) - 3]) == 0) {
-	sprintf(c_type, "application/x-gzip");
-    } else if (strcmp(".png", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "image/png");
-    } else if (strcmp(".svg", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "image/svg+xml");
-    } else if (strcmp(".oga", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "audio/ogg");
-    } else if (strcmp(".ico", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "image/x-icon");
-    } else if (strcmp(".ttf", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "font/ttf");
-    } else if (strcmp(".woff", &temp_url[strlen(temp_url) - 5]) == 0) {
-	sprintf(c_type, "font/woff");
-    } else if (strcmp(".woff2", &temp_url[strlen(temp_url) - 6]) == 0) {
-	sprintf(c_type, "font/woff2");
-    } else if (strcmp(".xml", &temp_url[strlen(temp_url) - 4]) == 0) {
-	sprintf(c_type, "text/xml");
-    } else {
-	sprintf(c_type, "application/octet-stream");
-	printf("Unknown content type for %s\n", temp_url);
-    }
+    /*
+     * Now the real file send in chunks.
+     */
+    char *chunk = malloc(1024);
+    ssize_t read_bytes;
+    do {
+        read_bytes = read(fd, chunk, 1024);
+        if (read_bytes == -1) {
+            ESP_LOGE(TAG, "files_handler() Failed to read file : %s", (send_gz) ? temp_url_gz : temp_url);
+        } else if (read_bytes > 0) {
+            /* Send the buffer contents as HTTP response chunk */
+            if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) {
+                close(fd);
+                ESP_LOGE(TAG, "files_handler() File sending failed!");
+                /* Abort sending file */
+                httpd_resp_sendstr_chunk(req, NULL);
+                /* Respond with 500 Internal Server Error */
+                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
+                return ESP_FAIL;
+            }
+        }
+    } while (read_bytes > 0);
+    /* Close file after sending complete */
+    close(fd);
 
-    vTaskDelay(2 / portTICK_PERIOD_MS);
-    //  httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate");
-    if (filesize) {
-	sprintf(header, "HTTP/1.1 200 OK\r\nLast-Modified: %s\r\nContent-Length: %ld\r\nContent-type: %s\r\n", strftime_buf, filesize,  c_type);
-    } else {
-	sprintf(header, "HTTP/1.1 200 OK\r\nContent-type: %s\r\n", c_type);
-    }
-    if (send_gz) {
-	strncat(header, "Content-Encoding: gzip\r\n", 255 - strlen(header));
-    }
-    strncat(header, "\r\n", 255 - strlen(header));	// Add last empty line.
-    err = netconn_write(conn, header, strlen(header), NETCONN_NOCOPY);
-    if (err != ERR_OK) {
-	ESP_LOGW(TAG, "%s sendfile %s%s err=%d on header write", ipstr, temp_url, (send_gz) ? ".gz":"", err);
-	fclose(f);
-	return;
-    }
-    // if (strstr(acceptEncodingBuffer, "gzip") == NULL)
-    // http_error(conn, 501, "Not implemented", "Your browser does not accept gzip-compressed data.");
-
-    sentsize = 0;
-    uint8_t	*buff = malloc(1024);
-    size_t      bytes;
-    int		pause = 0;
-
-    for (;;) {
-	bytes = fread(buff, 1, 1024, f);
-	if (bytes == 0)
-		break;
-
-	err = netconn_write(conn, buff, bytes, NETCONN_NOCOPY);
-	if (err != ERR_OK) {
-	    ESP_LOGW(TAG, "%s sendfile %s%s err=%d send %u bytes of %ld bytes", ipstr, temp_url,  (send_gz) ? ".gz":"", err, sentsize, filesize);
-	    break;
-	}
-	vTaskDelay(2 / portTICK_PERIOD_MS);
-	sentsize += bytes;
-	pause++;
-	if (pause > 50) { // 50 K
-	    pause = 0;
-	    vTaskDelay(50 / portTICK_PERIOD_MS);
-	}
-    }
-    fclose(f);
-    free(buff);
-	
-    if (sentsize == filesize) {
-	ESP_LOGI(TAG, "%s sendfile %s%s sent %u bytes, ok (%s)", ipstr, temp_url,  (send_gz) ? ".gz":"", sentsize, url);
-    }
+    /* Respond with an empty chunk to signal HTTP response completion */
+    httpd_resp_send_chunk(req, NULL, 0);
+    return ESP_OK;
 }
 
 
-
 /**
- * @brief Handle web ui websocket events.
+ * @brief Handle 'GET /logfiles.json'
  */
-void websock_callback(uint8_t num, WEBSOCKET_TYPE_t type, char* msg, uint64_t len)
+static esp_err_t logfiles_handler(httpd_req_t *req)
 {
-    char	jbuf[128];
+    ls_list     *lsx = NULL, *tmp;
+    char        temp[64];
+    FILE        *dest = fopen("/spiffs/w/logfiles.json", "w");
+
+    if (dest) {
+        DIR *dir = opendir("/sdcard/w/log");
+        if (dir) {
+            struct dirent* de = readdir(dir);
+            struct stat st;
+            while (de) {
+                snprintf(temp, 63, "/sdcard/w/log/");
+                strncat(temp, de->d_name, 63 - strlen(temp));
+                if (stat(temp, &st) == ESP_OK) {
+                    fill_list(&lsx, de->d_name, st.st_size, st.st_mtime);
+                }
+                de = readdir(dir);
+                vTaskDelay(5 / portTICK_PERIOD_MS);
+            }
+            closedir(dir);
+        } else {
+            ESP_LOGE(TAG, "Error %d open directory /sdcard/w/log", errno);
+        }
 
-    switch(type) {
-	case WEBSOCKET_CONNECT:
-	case WEBSOCKET_DISCONNECT_EXTERNAL:
-			break;
+        sort_list(&lsx);
+        bool comma = false;
+        fprintf(dest, "{\"Dir\":[{\"Folder\":\"/log\",\"Files\":[");
+        for (tmp = lsx; tmp; tmp = tmp->next) {
+            fprintf(dest, "%s{\"File\":\"%s\",\"Size\":%ld,\"Date\":%ld}", (comma)?",":"", tmp->d_name, tmp->size, tmp->mtime);
+            comma = true;
+        }
+        fprintf(dest, "]}]}");
+        fclose(dest);
+        tidy_lslist(&lsx);
+    } else {
+        ESP_LOGE(TAG, "Error %d write /spiffs/w/logfiles.json", errno);
+	httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
+        return ESP_FAIL;
+    }
 
-	case WEBSOCKET_DISCONNECT_INTERNAL:
-			ESP_LOGI(TAG,"Websocket client %i was disconnected",num);
-			break;
+    /*
+     * File is ready, send it using the files_handler().
+     */
+    esp_err_t ret = files_handler(req);
+    unlink("/spiffs/w/logfiles.json");
+    return ret;
+}
 
-	case WEBSOCKET_DISCONNECT_ERROR:
-			ESP_LOGI(TAG,"Websocket client %i was disconnected due to an error",num);
-			break;
+
+esp_err_t start_webserver(void)
+{
+    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+    config.lru_purge_enable = true;
+    config.uri_match_fn = httpd_uri_match_wildcard;
 
-	case WEBSOCKET_TEXT:
-			/*
-			 * Handle json actions from the web clients, like button presses.
-			 */
-			if (len < 128) { // Safety, messages are small.
-			    memcpy(jbuf, msg, len);
-                            jbuf[len] = '\0';
-			    if ((root = cJSON_Parse(jbuf))) {
-				if ((touch = cJSON_GetObjectItem(root,"touch"))) {
-				    int x = cJSON_GetObjectItem(touch, "x")->valueint;
-				    int y = cJSON_GetObjectItem(touch, "y")->valueint;
-				    WS_touched(x, y);
-				    break;
-				} else {
-				    ESP_LOGI(TAG,"not json touch");
-				}
-				cJSON_Delete(root);
-			    } else {
-				ESP_LOGI(TAG,"not json");
-			    }
-			}
-			// Log if the message in not processed.
-			ESP_LOGI(TAG,"Websocket client %i sent text message of size %i:\n%s",num,(unsigned)len,msg);
-			break;
+    // Start the httpd server
+    if (web_server == NULL) {
+	ESP_LOGI(TAG, "Starting httpd on port: '%d'", config.server_port);
+    	if (httpd_start(&web_server, &config) == ESP_OK) {
+            // Set URI handlers
+            ESP_LOGD(TAG, "Registering URI handlers");
+
+	    httpd_uri_t ws = {
+		.uri        = "/ws",
+		.method     = HTTP_GET,
+		.handler    = ws_handler,
+		.user_ctx   = NULL,
+		.is_websocket = true
+	    };
+	    httpd_register_uri_handler(web_server, &ws);
+
+	    httpd_uri_t logfiles = {
+		.uri = "/logfiles.json",
+		.method = HTTP_GET,
+		.handler = logfiles_handler,
+		.user_ctx   = NULL
+	    };
+	    httpd_register_uri_handler(web_server, &logfiles);
 
-	case WEBSOCKET_BIN:
-			ESP_LOGI(TAG,"Websocket client %i sent bin message of size %i:\n",num,(unsigned)len);
-			break;
+	    httpd_uri_t vfs_files = {
+            	.uri = "/*",
+            	.method = HTTP_GET,
+            	.handler = files_handler,
+            	.user_ctx = NULL
+	    };
+	    httpd_register_uri_handler(web_server, &vfs_files);
+            return ESP_OK;
+	}
+    } else {
+	ESP_LOGI(TAG, "httpd server already started");
+	return ESP_OK;
+    }
+
+    ESP_LOGI(TAG, "Error starting server!");
+    return ESP_FAIL;
+}
+
 
-	case WEBSOCKET_PING:
-			ESP_LOGI(TAG,"client %i pinged us with message of size %i:\n%s",num,(unsigned)len,msg);
-			break;
+static esp_err_t stop_webserver(void)
+{
+    esp_err_t	ret;
+
+    ESP_LOGI(TAG, "Stopping httpd server");
+    ret = httpd_stop(web_server);
+    web_server = NULL;
+    return ret;
+}
 
-	case WEBSOCKET_PONG:
-			ESP_LOGI(TAG,"client %i responded to the ping",num);
-			break;
+
+static void disconnect_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
+{
+    if (web_server) {
+        if (stop_webserver() == ESP_OK) {
+            web_server = NULL;
+        } else {
+            ESP_LOGE(TAG, "Failed to stop http server");
+        }
     }
 }
 
 
-
-#if 0
-void dump_buf(char *buf, uint16_t buflen)
+static void connect_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
 {
-    int i = 0, l = 1;
-
-    printf("request length %d\n00: ", buflen);
-    for (;;) {
-	if (i >= buflen)
-	    break;
-	if ((buf[i] < ' ') || (buf[i] > 126)) {
-	    if (buf[i] == '\n') {
-		printf("\\n\n%02d: ", l);
-		l++;
-	    } else if (buf[i] == '\r') {
-		printf("\\r");
-	    } else {
-		printf("\\%02x", buf[i]);
-	    }
-	} else {
-	    printf("%c", buf[i]);
-	}
-	i++;
-    }
-    printf("\n");
+    start_webserver();
 }
-#endif
-
 
 
 /**
- * @brief Serve any client.
- * @param http_client_sock The socket on which the client is connected.
+ * @brief Install the HTTP server event handlers.
+ *        The real server is started and stopped on events.
  */
-static void http_serve(struct netconn *conn)
+void install_http_server(void)
 {
-    char		ipstr[IPADDR_STRLEN_MAX];
-    struct netbuf	*inbuf;
-    static char		*buf;
-    static uint16_t	buflen;
-    static err_t	err;
-    char		url[128], *p, *mod_since = NULL;
-    ip_addr_t		remote_addr;
-    uint16_t		remote_port;
-    ls_list		*lsx = NULL, *tmp;
-
-    if (netconn_getaddr(conn, &remote_addr, &remote_port, 0) == ERR_OK) {
-	strcpy(ipstr, ip4addr_ntoa(ip_2_ip4(&remote_addr)));
-    } else {
-	ipstr[0] = '\0';
-    }
-
-    netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second
-    err = netconn_recv(conn, &inbuf);
-
-    if (err != ERR_OK) {
-	if (err != ERR_TIMEOUT) {	// Ignore timeout
-	    ESP_LOGW(TAG,"%s error %d on read", ipstr, err);
-	}
-	netconn_close(conn);
-	netconn_delete(conn);
-	netbuf_delete(inbuf);
-	return;
-    }
-
-    netbuf_data(inbuf, (void**)&buf, &buflen);
-
-    if (buf) {
-	/*
-	 * Build url string from the data buffer.
-	 * It looks like: GET /app/localization.js HTTP/1.1
-	 */
-	for (int i = 0; i < 10; i++) {
-	    if (buf[i] == ' ') {
-		i++;
-		for (int j = i; j < 128; j++) {
-		    url[j-i] = buf[j];
-		    if (url[j-i] == ' ') {
-			url[j-i] = '\0';
-			break;
-		    }
-		}
-		break;
-	    }
-	}
+    ESP_LOGI(TAG, "Install HTTP server");
 
-	if (strstr(buf, "GET /logfiles.json")) {
-	    char temp[64];
-	    FILE *dest = fopen("/spiffs/w/logfiles.json", "w");
-	    if (dest) {
-	    	DIR *dir = opendir("/sdcard/w/log");
-	    	if (dir) {
-		    struct dirent* de = readdir(dir);
-		    struct stat st;
-		    while (de) {
-		    	snprintf(temp, 63, "/sdcard/w/log/");
-			strncat(temp, de->d_name, 63 - strlen(temp));
-		    	if (stat(temp, &st) == ESP_OK) {
-			    fill_list(&lsx, de->d_name, st.st_size, st.st_mtime);
-		    	}
-		    	de = readdir(dir);
-		    	vTaskDelay(5 / portTICK_PERIOD_MS);
-		    }
-		    closedir(dir);
-	    	} else {
-		    ESP_LOGE(TAG, "Error %d open directory /sdcard/w/log", errno);
-		}
-
-		sort_list(&lsx);
-		bool comma = false;
-		fprintf(dest, "{\"Dir\":[{\"Folder\":\"/log\",\"Files\":[");
-		for (tmp = lsx; tmp; tmp = tmp->next) {
-		    fprintf(dest, "%s{\"File\":\"%s\",\"Size\":%ld,\"Date\":%ld}", (comma)?",":"", tmp->d_name, tmp->size, tmp->mtime);
-		    comma = true;
-		}
-	    	fprintf(dest, "]}]}");
-	    	fclose(dest);
-		tidy_lslist(&lsx);
-	    } else {
-		ESP_LOGE(TAG, "Error %d write /spiffs/w/logfiles.json", errno);
-	    }
-	    http_sendfile(conn, (char *)"/logfiles.json", NULL, ipstr);
-	    netconn_close(conn);
-	    netconn_delete(conn);
-	    netbuf_delete(inbuf);
-	    unlink("/spiffs/w/logfiles.json");
-	    return;
-	}
-
-	// http requests
-	if (! strstr(buf,"Upgrade: websocket")) {	// Not websocket
-	    p = strstr(buf, "If-Modified-Since:");	// May be cached
-	    if (p) {
-		size_t mod_len = strcspn(p, " ");
-		p += (int)(mod_len + 1);
-		mod_len = strcspn(p, "\r\n");
-		mod_since = malloc(mod_len + 2);
-		memcpy(mod_since, p, mod_len);
-		mod_since[mod_len] = '\0';
-	    }
-	    http_sendfile(conn, url, mod_since, ipstr);
-	    if (mod_since)
-	        free(mod_since);
-	    mod_since = NULL;
-	    netconn_close(conn);
-	    netconn_delete(conn);
-	    netbuf_delete(inbuf);
-	    return;
-	}
-
-	// websocket for web UI.
-	if ((strstr(buf,"GET /ws ") && strstr(buf,"Upgrade: websocket"))) {
-	    int nr = ws_server_add_client_protocol(conn, buf, buflen, (char *)"/ws", (char *)"binary", websock_callback);
-	    ESP_LOGI(TAG, "%s new websocket on /ws client: %d", ipstr, nr);
-	    netbuf_delete(inbuf);
-	    TFTstartWS(nr);
-	    // Startup something? Init webscreen?
-	    return;
-	}
-
-#if 0
-	dump_buf(buf, buflen);
-#endif
-
-	if (strstr(buf, "GET /")) {
-		ESP_LOGI(TAG, "%s request: %s", ipstr, buf);
-		http_error(conn, 404, (char *)"Not found", (char *)"Not found");
-	} else {
-		http_error(conn, 405, (char *)"Invalid method", (char *)"Invalid method");
-	}
-    }
-
-    netconn_close(conn);
-    netconn_delete(conn);
-    netbuf_delete(inbuf);
+    /*
+     * Respond to WiFi and network events. This is much better then letting the
+     * server run forever.
+     */
+    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, web_server));
+    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_LOST_IP, &disconnect_handler, web_server));
+    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, web_server));
 }
 
 
-
-/**
- * @brief Handles clients when they first connect. passes to a queue
- */
-static void task_HTTPserver(void* pvParameters)
-{
-    struct netconn	*conn, *newconn;
-    static err_t	err;
-
-    ESP_LOGI(TAG, "Start HTTP/web server");
-
-    conn = netconn_new(NETCONN_TCP);
-    netconn_bind(conn,NULL,80);
-    netconn_listen(conn);
-
-    do {
-	err = netconn_accept(conn, &newconn);
-	if (err == ERR_OK) {
-	    if (xQueueSendToBack(client_queue,&newconn,portMAX_DELAY) != pdTRUE) {
-		ESP_LOGW(TAG, "xQueueSendToBack() queue full");	    
-	    };
-	}
-	vTaskDelay(5 / portTICK_PERIOD_MS);
-    } while (err == ERR_OK);
-
-    ESP_LOGE(TAG, "Stopping HTTP/web server");
-    netconn_close(conn);
-    netconn_delete(conn);
-    vTaskDelete(NULL);
-}
-
-
-
-/**
- * @brief Receives clients from queue and handle them.
- */
-static void task_Queue(void* pvParameters)
-{
-    struct netconn* conn;
-
-    ESP_LOGI(TAG, "Start Queue task");
-    for(;;) {
-	xQueueReceive(client_queue, &(conn), portMAX_DELAY);
-	if (!conn)
-	    continue;
-	http_serve(conn);
-	vTaskDelay(2 / portTICK_PERIOD_MS);
-    }
-    ESP_LOGE(TAG, "Stopping Queue task");
-    vTaskDelete(NULL);
-}
-
-
-
-void start_http_websocket(void)
-{
-    ESP_LOGI(TAG, "Start HTTP/Websocket server");
-
-    client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*));
-    if (client_queue == 0) {
-        ESP_LOGE(TAG, "Failed to create client_queue");
-        return;
-    }
-
-    ws_server_start();
-    xTaskCreate(&task_HTTPserver, "HTTPserver", 5000, NULL, 9, &xTaskHTTP);
-    xTaskCreate(&task_Queue,      "Queue",      6000, NULL, 6, &xTaskQueue);
-}
-
--- a/main/task_http.h	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/task_http.h	Wed Jul 03 20:01:31 2024 +0200
@@ -8,8 +8,26 @@
 
 
 /**
- * @brief Start the HTTP/Websocket server.
+ * @brief Send text to client with the set number.
+ * @param num The client connection number.
+ * @param msg The message to send.
+ * @param len The length of the message to send.
+ * @return 1 if message send.
  */
-void start_http_websocket(void);
+int ws_server_send_text_client(int num, char* msg);
+
+
+/**
+ * @brief Sends text to all clients with the set url.
+ * @param msg The message to send.
+ * @return The number of clients the message was send to.
+ */
+int ws_server_send_text_clients(char* msg);
+
+
+/**
+ * @brief Install the HTTP/Websocket server.
+ */
+void install_http_server(void);
 
 #endif
--- a/main/task_sound.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/task_sound.c	Wed Jul 03 20:01:31 2024 +0200
@@ -48,7 +48,7 @@
     for (int i = 1; i <= sound[0]; i += 2) {
 	if (sound != _sound_Startup) {
 	    snprintf(msg, 15, "{\"beep\":\"1\"}");
-    	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+    	    ws_server_send_text_clients(msg);
 	}
 	gpio_set_level(PIEZO_BUZZER, 1);
 	vTaskDelay(sound[i] / portTICK_PERIOD_MS);
--- a/main/task_tft.c	Mon Jul 01 08:38:57 2024 +0200
+++ b/main/task_tft.c	Wed Jul 03 20:01:31 2024 +0200
@@ -227,7 +227,7 @@
     	TFT_print(s_timer, X, Y);
 	_oldTime = Time;
 	snprintf(msg, 31, "{\"timer\":\"%s\"}", s_timer);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 }
 
@@ -245,7 +245,7 @@
     TFT_print(s_top_msg, CENTER, 2);
     font_transparent = 0;
     snprintf(msg, 95, "{\"top_msg\":\"%s\"}", s_top_msg);
-    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+    ws_server_send_text_clients(msg);
 }
 
 
@@ -295,7 +295,7 @@
 	}
 	lon = con;
 	snprintf(msg, 95, "{\"mlt_led\":\"%s\"}", con ? "1":"0");
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 
     cpump = (Pump_pin) ? true : false;
@@ -307,7 +307,7 @@
 	}
 	lpump = cpump;
 	snprintf(msg, 95, "{\"pump_led\":\"%s\"}", cpump ? "1":"0");
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 
     if (equipment.SSR2 == SSR2_ON_IDLE) {
@@ -320,7 +320,7 @@
 	    }
 	    lpwr = cpwr;
 	    snprintf(msg, 95, "{\"hlt_led\":\"%s\"}", cpwr ? "1":"0");
-	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	    ws_server_send_text_clients(msg);
 	}
     }
 
@@ -329,7 +329,7 @@
     	TFT_print(ctemp, x + 5, y + 23);
 	strncpy(ltemp, ctemp, 16);
 	snprintf(msg, 95, "{\"mlt_pv\":\"%s\"}", ctemp);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 
     TFT_setFont(DEJAVU18_FONT, NULL);
@@ -338,14 +338,14 @@
     	TFT_print(csp, x + 5, y + 70);
 	strncpy(lsp, csp, 16);
 	snprintf(msg, 95, "{\"mlt_sp\":\"%s\"}", csp);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
     if (strcmp(cpower, lpower) || (! update)) {
     	TFT_clearStringRect(x + 120, y + 70, (char *)"100%");
     	TFT_print(cpower, x + 120, y + 70);
 	strncpy(lpower, cpower, 16);
 	snprintf(msg, 95, "{\"mlt_power\":\"%s\"}", cpower);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 }
 
@@ -396,7 +396,7 @@
 	}
 	lon = con;
 	snprintf(msg, 95, "{\"hlt_led\":\"%s\"}", con ? "1":"0");
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 
     if (strcmp(ltemp, ctemp) || (! update)) {
@@ -409,7 +409,7 @@
 	}
 	strncpy(ltemp, ctemp, 16);
 	snprintf(msg, 95, "{\"hlt_pv\":\"%s\"}", ctemp);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 
     H = (small) ? 50 : 70;
@@ -419,14 +419,14 @@
 	TFT_print(csp, x + 5, y + H);
 	strncpy(lsp, csp, 16);
 	snprintf(msg, 95, "{\"hlt_sp\":\"%s\"}", csp);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
     if (strcmp(cpower, lpower) || (! update)) {
 	TFT_clearStringRect(x + 120, y + H, (char *)"100%");
 	TFT_print(cpower, x + 120, y + H);
 	strncpy(lpower, cpower, 16);
 	snprintf(msg, 95, "{\"hlt_power\":\"%s\"}", cpower);
-	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	ws_server_send_text_clients(msg);
     }
 }
 
@@ -485,7 +485,7 @@
 		    s_timer, s_top_msg);
 
     	xSemaphoreGive(xSemaphoreDriver);
-	ws_server_send_text_client(client, msg, strlen(msg));
+	ws_server_send_text_client(client, msg);
     }
 
 }
@@ -531,7 +531,7 @@
 	     */
 	    Sub_Screen = 0;
 	    snprintf(msg, 95, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+	    ws_server_send_text_clients(msg);
 
 	    ESP_LOGD(TAG, "Change screen %d to %d", Old_Screen, Main_Screen);
 	    _bg = TFT_BLACK;
@@ -567,8 +567,8 @@
 			//         -------------------------------------
 			_fg = TFT_ORANGE;
 			TFT_print((char *)"Parts are written by Chris Morgan,\r\n", 0, LASTY);
-			TFT_print((char *)"Brett Beauregard, Blake Felt, LoBo,\r\n", 0, LASTY);
-			TFT_print((char *)"and David Antliff.\r\n", 0, LASTY);
+			TFT_print((char *)"Brett Beauregard, LoBo and David\r\n", 0, LASTY);
+			TFT_print((char *)"Antliff.\r\n", 0, LASTY);
 			ShowInteger(1,140, (char *)"Free memory", (char *)" bytes", esp_get_free_heap_size());
 			ShowText(1,158, (char *)"IDF version", (char *)esp_get_idf_version());
 			Buttons_Add(130, 200, 60, 40, (char *)"Ok", 0);
@@ -676,7 +676,7 @@
 		    snprintf(msg, 95, " %s ", strftime_buf);
 		    TFT_print(msg, CENTER, 125);
 		    snprintf(msg, 95, "{\"timer\":\"%s\"}", strftime_buf);	// Fix string termination and only send once/second.
-		    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+		    ws_server_send_text_clients(msg);
 		}
 		break;
 
--- a/sdkconfig	Mon Jul 01 08:38:57 2024 +0200
+++ b/sdkconfig	Wed Jul 03 20:01:31 2024 +0200
@@ -741,7 +741,7 @@
 # CONFIG_HTTPD_ERR_RESP_NO_DELAY is not set
 CONFIG_HTTPD_PURGE_BUF_LEN=32
 # CONFIG_HTTPD_LOG_PURGE_DATA is not set
-# CONFIG_HTTPD_WS_SUPPORT is not set
+CONFIG_HTTPD_WS_SUPPORT=y
 # CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set
 CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000
 # end of HTTP Server
@@ -1811,16 +1811,6 @@
 CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
 # CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
 # end of Wi-Fi Provisioning Manager
-
-#
-# WebSocket Server
-#
-CONFIG_WEBSOCKET_SERVER_MAX_CLIENTS=10
-CONFIG_WEBSOCKET_SERVER_QUEUE_SIZE=10
-CONFIG_WEBSOCKET_SERVER_QUEUE_TIMEOUT=30
-CONFIG_WEBSOCKET_SERVER_TASK_STACK_DEPTH=7000
-CONFIG_WEBSOCKET_SERVER_TASK_PRIORITY=5
-# end of WebSocket Server
 # end of Component config
 
 # CONFIG_IDF_EXPERIMENTAL_FEATURES is not set

mercurial