Add connect to Wifi and File Manager Webserver (#41)
## Summary
- **What is the goal of this PR?**
Implements wireless EPUB file management via a built-in web server,
enabling users to upload, browse, organize, and delete EPUB files from
any device on the same WiFi network without needing a computer cable
connection.
- **What changes are included?**
- **New Web Server**
([`CrossPointWebServer.cpp`](src/CrossPointWebServer.cpp),
[`CrossPointWebServer.h`](src/CrossPointWebServer.h)):
- HTTP server on port 80 with a responsive HTML/CSS interface
- Home page showing device status (version, IP, free memory)
- File Manager with folder navigation and breadcrumb support
- EPUB file upload with progress tracking
- Folder creation and file/folder deletion
- XSS protection via HTML escaping
- Hidden system folders (`.` prefixed, "System Volume Information",
"XTCache")
- **WiFi Screen** ([`WifiScreen.cpp`](src/screens/WifiScreen.cpp),
[`WifiScreen.h`](src/screens/WifiScreen.h)):
- Network scanning with signal strength indicators
- Visual indicators for encrypted (`*`) and saved (`+`) networks
- State machine managing: scanning, network selection, password entry,
connecting, save/forget prompts
- 15-second connection timeout handling
- Integration with web server (starts on connect, stops on exit)
- **WiFi Credential Storage**
([`WifiCredentialStore.cpp`](src/WifiCredentialStore.cpp),
[`WifiCredentialStore.h`](src/WifiCredentialStore.h)):
- Persistent storage in `/sd/.crosspoint/wifi.bin`
- XOR obfuscation for stored passwords (basic protection against casual
reading)
- Up to 8 saved networks with add/remove/update operations
- **On-Screen Keyboard**
([`OnScreenKeyboard.cpp`](src/screens/OnScreenKeyboard.cpp),
[`OnScreenKeyboard.h`](src/screens/OnScreenKeyboard.h)):
- Reusable QWERTY keyboard component with shift support
- Special keys: Shift, Space, Backspace, Done
- Support for password masking mode
- **Settings Screen Integration**
([`SettingsScreen.h`](src/screens/SettingsScreen.h)):
- Added WiFi action to navigate to the new WiFi screen
- **Documentation** ([`docs/webserver.md`](docs/webserver.md)):
- Comprehensive user guide covering WiFi setup, web interface usage,
file management, troubleshooting, and security notes
- See this for more screenshots!
- Working "displays the right way in GitHub" on my repo:
https://github.com/olearycrew/crosspoint-reader/blob/feature/connect-to-wifi/docs/webserver.md
**Video demo**
https://github.com/user-attachments/assets/283e32dc-2d9f-4ae2-848e-01f41166a731
## Additional Context
- **Security considerations**: The web server has no
authentication—anyone on the same WiFi network can access files. This is
documented as a limitation, recommending use only on trusted private
networks. Password obfuscation in the credential store is XOR-based, not
cryptographically secure.
- **Memory implications**: The web server and WiFi stack consume
significant memory. The implementation properly cleans up (stops server,
disconnects WiFi, sets `WIFI_OFF` mode) when exiting the WiFi screen to
free resources.
- **Async operations**: Network scanning and connection use async
patterns with FreeRTOS tasks to prevent blocking the UI. The display
task handles rendering on a dedicated thread with mutex protection.
- **Browser compatibility**: The web interface uses standard
HTML5/CSS3/JavaScript and is tested to work with all modern browsers on
desktop and mobile.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-19 09:05:43 -05:00
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
SRC_DIR = "src"
|
|
|
|
|
|
2025-12-23 14:34:40 +02:00
|
|
|
|
|
|
|
|
def bytes_to_cpp_byte_array(input_file_bytes: bytes, input_file_path: str, output_header_file: str, variable_name: str):
|
|
|
|
|
# Format bytes into C++ byte array initialiser format (hex values)
|
|
|
|
|
hex_values = [f"0x{b:02x}" for b in input_file_bytes]
|
|
|
|
|
bytes_array_declaration = ", ".join(hex_values)
|
|
|
|
|
data_length = len(input_file_bytes)
|
|
|
|
|
|
|
|
|
|
# Generate the C++ header file content
|
|
|
|
|
header_content = f"""
|
|
|
|
|
#ifndef {variable_name.upper()}_H
|
|
|
|
|
#define {variable_name.upper()}_H
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
// Embedded file: {os.path.basename(input_file_path)}
|
|
|
|
|
constexpr uint8_t {variable_name}_data[] = {{
|
|
|
|
|
{bytes_array_declaration}
|
|
|
|
|
}};
|
|
|
|
|
constexpr size_t {variable_name}_size = {data_length};
|
|
|
|
|
|
|
|
|
|
#endif // {variable_name.upper()}_H
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(output_header_file, 'w') as f:
|
|
|
|
|
f.write(header_content)
|
|
|
|
|
print(f"Successfully generated C++ header file: {output_header_file}")
|
|
|
|
|
except IOError as e:
|
|
|
|
|
print(f"Error writing header file: {e}")
|
|
|
|
|
exit(1)
|
|
|
|
|
|
Add connect to Wifi and File Manager Webserver (#41)
## Summary
- **What is the goal of this PR?**
Implements wireless EPUB file management via a built-in web server,
enabling users to upload, browse, organize, and delete EPUB files from
any device on the same WiFi network without needing a computer cable
connection.
- **What changes are included?**
- **New Web Server**
([`CrossPointWebServer.cpp`](src/CrossPointWebServer.cpp),
[`CrossPointWebServer.h`](src/CrossPointWebServer.h)):
- HTTP server on port 80 with a responsive HTML/CSS interface
- Home page showing device status (version, IP, free memory)
- File Manager with folder navigation and breadcrumb support
- EPUB file upload with progress tracking
- Folder creation and file/folder deletion
- XSS protection via HTML escaping
- Hidden system folders (`.` prefixed, "System Volume Information",
"XTCache")
- **WiFi Screen** ([`WifiScreen.cpp`](src/screens/WifiScreen.cpp),
[`WifiScreen.h`](src/screens/WifiScreen.h)):
- Network scanning with signal strength indicators
- Visual indicators for encrypted (`*`) and saved (`+`) networks
- State machine managing: scanning, network selection, password entry,
connecting, save/forget prompts
- 15-second connection timeout handling
- Integration with web server (starts on connect, stops on exit)
- **WiFi Credential Storage**
([`WifiCredentialStore.cpp`](src/WifiCredentialStore.cpp),
[`WifiCredentialStore.h`](src/WifiCredentialStore.h)):
- Persistent storage in `/sd/.crosspoint/wifi.bin`
- XOR obfuscation for stored passwords (basic protection against casual
reading)
- Up to 8 saved networks with add/remove/update operations
- **On-Screen Keyboard**
([`OnScreenKeyboard.cpp`](src/screens/OnScreenKeyboard.cpp),
[`OnScreenKeyboard.h`](src/screens/OnScreenKeyboard.h)):
- Reusable QWERTY keyboard component with shift support
- Special keys: Shift, Space, Backspace, Done
- Support for password masking mode
- **Settings Screen Integration**
([`SettingsScreen.h`](src/screens/SettingsScreen.h)):
- Added WiFi action to navigate to the new WiFi screen
- **Documentation** ([`docs/webserver.md`](docs/webserver.md)):
- Comprehensive user guide covering WiFi setup, web interface usage,
file management, troubleshooting, and security notes
- See this for more screenshots!
- Working "displays the right way in GitHub" on my repo:
https://github.com/olearycrew/crosspoint-reader/blob/feature/connect-to-wifi/docs/webserver.md
**Video demo**
https://github.com/user-attachments/assets/283e32dc-2d9f-4ae2-848e-01f41166a731
## Additional Context
- **Security considerations**: The web server has no
authentication—anyone on the same WiFi network can access files. This is
documented as a limitation, recommending use only on trusted private
networks. Password obfuscation in the credential store is XOR-based, not
cryptographically secure.
- **Memory implications**: The web server and WiFi stack consume
significant memory. The implementation properly cleans up (stops server,
disconnects WiFi, sets `WIFI_OFF` mode) when exiting the WiFi screen to
free resources.
- **Async operations**: Network scanning and connection use async
patterns with FreeRTOS tasks to prevent blocking the UI. The display
task handles rendering on a dedicated thread with mutex protection.
- **Browser compatibility**: The web interface uses standard
HTML5/CSS3/JavaScript and is tested to work with all modern browsers on
desktop and mobile.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-19 09:05:43 -05:00
|
|
|
def minify_html(html: str) -> str:
|
|
|
|
|
# Tags where whitespace should be preserved
|
|
|
|
|
preserve_tags = ['pre', 'code', 'textarea', 'script', 'style']
|
|
|
|
|
preserve_regex = '|'.join(preserve_tags)
|
|
|
|
|
|
|
|
|
|
# Protect preserve blocks with placeholders
|
|
|
|
|
preserve_blocks = []
|
|
|
|
|
def preserve(match):
|
|
|
|
|
preserve_blocks.append(match.group(0))
|
|
|
|
|
return f"__PRESERVE_BLOCK_{len(preserve_blocks)-1}__"
|
|
|
|
|
|
|
|
|
|
html = re.sub(rf'<({preserve_regex})[\s\S]*?</\1>', preserve, html, flags=re.IGNORECASE)
|
|
|
|
|
|
|
|
|
|
# Remove HTML comments
|
|
|
|
|
html = re.sub(r'<!--.*?-->', '', html, flags=re.DOTALL)
|
|
|
|
|
|
|
|
|
|
# Collapse all whitespace between tags
|
|
|
|
|
html = re.sub(r'>\s+<', '><', html)
|
|
|
|
|
|
|
|
|
|
# Collapse multiple spaces inside tags
|
|
|
|
|
html = re.sub(r'\s+', ' ', html)
|
|
|
|
|
|
|
|
|
|
# Restore preserved blocks
|
|
|
|
|
for i, block in enumerate(preserve_blocks):
|
|
|
|
|
html = html.replace(f"__PRESERVE_BLOCK_{i}__", block)
|
|
|
|
|
|
|
|
|
|
return html.strip()
|
|
|
|
|
|
2025-12-23 14:34:40 +02:00
|
|
|
def read_src_file(root: str, file: str) -> str:
|
|
|
|
|
file_path = os.path.join(root, file)
|
|
|
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
|
|
|
return f.read()
|
|
|
|
|
|
|
|
|
|
def build_static(src_path: str, dest_dir: str):
|
|
|
|
|
filename = os.path.basename(src_path)
|
|
|
|
|
base_name, extension = os.path.splitext(filename)
|
|
|
|
|
postfix = extension[1:].capitalize()
|
|
|
|
|
base_name = f"{os.path.splitext(filename)[0]}{postfix}"
|
|
|
|
|
output_cpp_header = os.path.join(dest_dir, f"{base_name}.generated.h")
|
|
|
|
|
cpp_variable_name = base_name
|
|
|
|
|
if not os.path.exists(dest_dir):
|
|
|
|
|
os.makedirs(dest_dir)
|
|
|
|
|
|
|
|
|
|
if extension == ".html":
|
|
|
|
|
# minify HTML content
|
|
|
|
|
src_string = open(src_path, "r").read()
|
|
|
|
|
minified_str = minify_html(src_string)
|
|
|
|
|
src_bytes = minified_str.encode("utf-8")
|
|
|
|
|
else:
|
|
|
|
|
src_bytes = open(src_path, "rb").read()
|
|
|
|
|
|
|
|
|
|
bytes_to_cpp_byte_array(
|
|
|
|
|
input_file_bytes=src_bytes,
|
|
|
|
|
input_file_path=src_path,
|
|
|
|
|
output_header_file=output_cpp_header,
|
|
|
|
|
variable_name=cpp_variable_name
|
|
|
|
|
)
|
|
|
|
|
|
Add connect to Wifi and File Manager Webserver (#41)
## Summary
- **What is the goal of this PR?**
Implements wireless EPUB file management via a built-in web server,
enabling users to upload, browse, organize, and delete EPUB files from
any device on the same WiFi network without needing a computer cable
connection.
- **What changes are included?**
- **New Web Server**
([`CrossPointWebServer.cpp`](src/CrossPointWebServer.cpp),
[`CrossPointWebServer.h`](src/CrossPointWebServer.h)):
- HTTP server on port 80 with a responsive HTML/CSS interface
- Home page showing device status (version, IP, free memory)
- File Manager with folder navigation and breadcrumb support
- EPUB file upload with progress tracking
- Folder creation and file/folder deletion
- XSS protection via HTML escaping
- Hidden system folders (`.` prefixed, "System Volume Information",
"XTCache")
- **WiFi Screen** ([`WifiScreen.cpp`](src/screens/WifiScreen.cpp),
[`WifiScreen.h`](src/screens/WifiScreen.h)):
- Network scanning with signal strength indicators
- Visual indicators for encrypted (`*`) and saved (`+`) networks
- State machine managing: scanning, network selection, password entry,
connecting, save/forget prompts
- 15-second connection timeout handling
- Integration with web server (starts on connect, stops on exit)
- **WiFi Credential Storage**
([`WifiCredentialStore.cpp`](src/WifiCredentialStore.cpp),
[`WifiCredentialStore.h`](src/WifiCredentialStore.h)):
- Persistent storage in `/sd/.crosspoint/wifi.bin`
- XOR obfuscation for stored passwords (basic protection against casual
reading)
- Up to 8 saved networks with add/remove/update operations
- **On-Screen Keyboard**
([`OnScreenKeyboard.cpp`](src/screens/OnScreenKeyboard.cpp),
[`OnScreenKeyboard.h`](src/screens/OnScreenKeyboard.h)):
- Reusable QWERTY keyboard component with shift support
- Special keys: Shift, Space, Backspace, Done
- Support for password masking mode
- **Settings Screen Integration**
([`SettingsScreen.h`](src/screens/SettingsScreen.h)):
- Added WiFi action to navigate to the new WiFi screen
- **Documentation** ([`docs/webserver.md`](docs/webserver.md)):
- Comprehensive user guide covering WiFi setup, web interface usage,
file management, troubleshooting, and security notes
- See this for more screenshots!
- Working "displays the right way in GitHub" on my repo:
https://github.com/olearycrew/crosspoint-reader/blob/feature/connect-to-wifi/docs/webserver.md
**Video demo**
https://github.com/user-attachments/assets/283e32dc-2d9f-4ae2-848e-01f41166a731
## Additional Context
- **Security considerations**: The web server has no
authentication—anyone on the same WiFi network can access files. This is
documented as a limitation, recommending use only on trusted private
networks. Password obfuscation in the credential store is XOR-based, not
cryptographically secure.
- **Memory implications**: The web server and WiFi stack consume
significant memory. The implementation properly cleans up (stops server,
disconnects WiFi, sets `WIFI_OFF` mode) when exiting the WiFi screen to
free resources.
- **Async operations**: Network scanning and connection use async
patterns with FreeRTOS tasks to prevent blocking the UI. The display
task handles rendering on a dedicated thread with mutex protection.
- **Browser compatibility**: The web interface uses standard
HTML5/CSS3/JavaScript and is tested to work with all modern browsers on
desktop and mobile.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-19 09:05:43 -05:00
|
|
|
for root, _, files in os.walk(SRC_DIR):
|
|
|
|
|
for file in files:
|
2025-12-23 14:34:40 +02:00
|
|
|
if file.endswith((".html", ".css", ".js")):
|
|
|
|
|
build_static(os.path.join(root, file), root)
|