microweb 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- microweb/cli.py +265 -57
- microweb/uploader.py +1 -1
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/METADATA +317 -20
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/RECORD +8 -8
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/WHEEL +0 -0
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/entry_points.txt +0 -0
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {microweb-0.1.2.dist-info → microweb-0.1.4.dist-info}/top_level.txt +0 -0
microweb/cli.py
CHANGED
@@ -46,7 +46,7 @@ def check_micropython(port):
|
|
46
46
|
print_colored(f"mpremote output:\n{result.stdout.strip()}\n{result.stderr.strip()}", color='yellow')
|
47
47
|
return False
|
48
48
|
except Exception as e:
|
49
|
-
print_colored(f"Error checking MicroPython via mpremote: {e}", color='
|
49
|
+
print_colored(f"Error checking MicroPython via mpremote: {e}. The app may be running boot.py.", color='blue')
|
50
50
|
return False
|
51
51
|
|
52
52
|
def get_remote_file_info(port):
|
@@ -183,31 +183,47 @@ def verify_static_files_exist(static_files, static_dir):
|
|
183
183
|
return existing_files, missing_files
|
184
184
|
|
185
185
|
def upload_boot_py(port, module_name):
|
186
|
-
"""Create and upload boot.py
|
186
|
+
"""Create and upload boot.py that imports the specified app module."""
|
187
187
|
boot_content = f"import {module_name}\n"
|
188
|
-
|
189
|
-
with
|
190
|
-
|
191
|
-
|
188
|
+
|
189
|
+
with open("boot.py", "w", encoding="utf-8") as f:
|
190
|
+
f.write(boot_content)
|
191
|
+
|
192
192
|
try:
|
193
|
-
print_colored(f"⬆️ Uploading boot.py to import {module_name}...", color='cyan')
|
194
|
-
upload_file(
|
193
|
+
print_colored(f"⬆️ Uploading boot.py to import '{module_name}'...", color='cyan')
|
194
|
+
upload_file("boot.py", port, destination='boot.py')
|
195
|
+
print_colored("✅ boot.py uploaded successfully.", color='green')
|
195
196
|
finally:
|
196
|
-
os.
|
197
|
+
os.remove("boot.py")
|
198
|
+
|
197
199
|
|
198
200
|
def remove_boot_py(port):
|
199
|
-
"""
|
201
|
+
"""Replace boot.py on ESP32 with minimal content using ampy."""
|
202
|
+
boot_content = "import gc\ngc.collect()\n"
|
203
|
+
boot_filename = "boot.py"
|
204
|
+
|
205
|
+
# Write minimal boot.py locally
|
206
|
+
with open(boot_filename, "w", encoding="utf-8") as f:
|
207
|
+
f.write(boot_content)
|
208
|
+
|
200
209
|
try:
|
201
|
-
print_colored("🗑️
|
202
|
-
cmd = [
|
203
|
-
result = subprocess.run(cmd, capture_output=True, text=True, timeout=
|
210
|
+
print_colored(f"🗑️ Replacing boot.py on ESP32 (port {port}) using ampy...", color='cyan')
|
211
|
+
cmd = ["ampy", "--port", port, "put", boot_filename]
|
212
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
213
|
+
|
204
214
|
if result.returncode != 0:
|
205
|
-
print_colored(f"⚠️
|
215
|
+
print_colored(f"⚠️ Failed to replace boot.py: {result.stderr.strip()}", color='yellow')
|
206
216
|
else:
|
207
|
-
print_colored("boot.py
|
217
|
+
print_colored("✅ boot.py replaced successfully.", color='green')
|
218
|
+
|
208
219
|
except Exception as e:
|
209
|
-
print_colored(f"Error
|
220
|
+
print_colored(f"❌ Error replacing boot.py: {e}", color='red')
|
210
221
|
|
222
|
+
finally:
|
223
|
+
if os.path.exists(boot_filename):
|
224
|
+
os.remove(boot_filename)
|
225
|
+
|
226
|
+
|
211
227
|
@click.group()
|
212
228
|
def cli():
|
213
229
|
pass
|
@@ -353,12 +369,13 @@ def run(file, port, check_only, static, force, no_stop, timeout, add_boot, remov
|
|
353
369
|
if not port:
|
354
370
|
print_colored("No ESP32 found. Specify --port, e.g., --port COM10.", color='red')
|
355
371
|
return
|
356
|
-
if not check_micropython(port):
|
357
|
-
print_colored(f"MicroPython not detected on ESP32. Please run 'microweb flash --port {port}' first.", color='red')
|
358
|
-
return
|
359
372
|
if remove_boot:
|
360
373
|
remove_boot_py(port)
|
361
374
|
return
|
375
|
+
if not check_micropython(port):
|
376
|
+
print_colored(f"MicroPython not detected on ESP32. Please run 'microweb flash --port {port}' first.", color='red')
|
377
|
+
return
|
378
|
+
|
362
379
|
try:
|
363
380
|
print_colored(f"\nGetting remote file information from {port}...", color='blue')
|
364
381
|
remote_files = get_remote_file_info(port)
|
@@ -458,6 +475,7 @@ def run(file, port, check_only, static, force, no_stop, timeout, add_boot, remov
|
|
458
475
|
if result.returncode != 0:
|
459
476
|
print_colored(f"❌ Error running {file}: return code {result.returncode}", color='red')
|
460
477
|
print_colored(f"stdout:\n{result.stdout.strip()}\nstderr:\n{result.stderr.strip()}", color='red')
|
478
|
+
print_colored(f"try microweb flash --port COM10 ",color="cyan")
|
461
479
|
return
|
462
480
|
if upload_count > 0:
|
463
481
|
print_colored(f"📊 Uploaded {upload_count} file(s), skipped {len(files_skipped)} file(s)", color='green')
|
@@ -472,6 +490,8 @@ def run(file, port, check_only, static, force, no_stop, timeout, add_boot, remov
|
|
472
490
|
except Exception as e:
|
473
491
|
print_colored(f"❌ Error: {e}", color='red')
|
474
492
|
|
493
|
+
|
494
|
+
|
475
495
|
@cli.command()
|
476
496
|
@click.option('--port', default=None, help='Serial port, e.g., COM10')
|
477
497
|
@click.option('--remove', 'remove_everything', is_flag=True, help='Actually remove all files in the ESP32 home directory')
|
@@ -489,51 +509,239 @@ def remove(port, remove_everything):
|
|
489
509
|
try:
|
490
510
|
if remove_everything:
|
491
511
|
print_colored("Removing all files in ESP32 home directory...", color='yellow')
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
cmd_rm = [
|
506
|
-
'mpremote', 'connect', port, 'exec',
|
507
|
-
f"import os; import shutil; "
|
508
|
-
f"shutil.rmtree('{filename}') if hasattr(__import__('shutil'), 'rmtree') and os.path.isdir('{filename}') "
|
509
|
-
f"else (os.remove('{filename}') if '{filename}' in os.listdir() else None)"
|
510
|
-
]
|
511
|
-
subprocess.run(cmd_rm, capture_output=True, text=True, timeout=10)
|
512
|
-
print_colored("All files in ESP32 home directory removed.", color='green')
|
513
|
-
else:
|
514
|
-
print_colored(f"Error listing files: {result.stderr}", color='red')
|
512
|
+
remote_files = get_remote_file_info(port)
|
513
|
+
if not remote_files:
|
514
|
+
print_colored("No files found or error accessing filesystem.", color='yellow')
|
515
|
+
return
|
516
|
+
for filename in remote_files.keys():
|
517
|
+
if filename in ('.', '..'):
|
518
|
+
continue
|
519
|
+
print_colored(f"Removing {filename}...", color='cyan')
|
520
|
+
cmd_rm = ['mpremote', 'connect', port, 'rm', filename]
|
521
|
+
result = subprocess.run(cmd_rm, capture_output=True, text=True, timeout=10)
|
522
|
+
if result.returncode != 0:
|
523
|
+
print_colored(f"Error removing {filename}: {result.stderr}", color='red')
|
524
|
+
print_colored("All files in ESP32 home directory removed.", color='green')
|
515
525
|
else:
|
516
526
|
print_colored("Dry run: No files were removed. Use --remove to actually delete all files in the ESP32 home directory.", color='yellow')
|
517
527
|
except Exception as e:
|
518
528
|
print_colored(f"Error removing files: {e}", color='red')
|
519
529
|
|
530
|
+
|
531
|
+
|
532
|
+
@cli.command()
|
533
|
+
@click.option('--path', default='example_app', show_default=True, help='Directory to create the example app')
|
534
|
+
def create(path):
|
535
|
+
"""Create an example MicroWeb app with app.py, static/index.html, and README.md."""
|
536
|
+
try:
|
537
|
+
os.makedirs(path, exist_ok=True)
|
538
|
+
os.makedirs(os.path.join(path, 'static'), exist_ok=True)
|
539
|
+
|
540
|
+
app_content = """import wifi
|
541
|
+
from microweb import MicroWeb
|
542
|
+
|
543
|
+
# Define a simple User model to store and retrieve user data
|
544
|
+
class User:
|
545
|
+
def __init__(self, user_id, name, email):
|
546
|
+
self.user_id = user_id
|
547
|
+
self.name = name
|
548
|
+
self.email = email
|
549
|
+
|
550
|
+
def to_dict(self):
|
551
|
+
return {
|
552
|
+
'user_id': self.user_id,
|
553
|
+
'name': self.name,
|
554
|
+
'email': self.email
|
555
|
+
}
|
556
|
+
|
557
|
+
# Initialize MicroWeb application with debug mode and Wi-Fi access point configuration
|
558
|
+
app = MicroWeb(debug=True, ap={"ssid": "MyESP32", "password": "mypassword"})
|
559
|
+
|
560
|
+
# Define route for the root URL, rendering the index.html template with a welcome message
|
561
|
+
@app.route('/')
|
562
|
+
def home(req):
|
563
|
+
return app.render_template('index.html', message="Welcome to MicroWeb API!")
|
564
|
+
|
565
|
+
# Define API route to return server status and IP address as JSON
|
566
|
+
@app.route('/api/status', methods=['GET'])
|
567
|
+
def status(req):
|
568
|
+
return app.json_response({"status": "running", "ip": wifi.get_ip()})
|
569
|
+
|
570
|
+
# Define API route to get user data using the User model
|
571
|
+
@app.route('/api/user/<id>')
|
572
|
+
def get_user(req, match):
|
573
|
+
# Extract ID from URL parameter, default to "Anonymous" if not provided
|
574
|
+
id = match.group(1) if match else "Anonymous"
|
575
|
+
# Create a sample user instance (in a real app, this could come from a database)
|
576
|
+
user = User(user_id=id, name=f"User {id}", email=f"user{id}@example.com")
|
577
|
+
return app.json_response(user.to_dict())
|
578
|
+
|
579
|
+
# Define API route to greet a user by ID
|
580
|
+
@app.route('/greet/<name>')
|
581
|
+
def greet(req, match):
|
582
|
+
name = match.group(1) if match else "Anonymous"
|
583
|
+
return {"message": f"Hello, {name}!", "status": "success"}
|
584
|
+
|
585
|
+
# Define API route to echo back POST data as JSON
|
586
|
+
# @app.route('/api/echo', methods=['POST'])
|
587
|
+
# def echo(req):
|
588
|
+
# data = req.form # Get form data from POST body
|
589
|
+
# return app.json_response({"received": data})
|
590
|
+
|
591
|
+
# Define a route that handles both GET and POST requests, returning method-specific JSON responses
|
592
|
+
# @app.route('/api/methods', methods=['GET', 'POST'])
|
593
|
+
# def methods(req):
|
594
|
+
# if req.method == 'GET':
|
595
|
+
# return app.json_response({"method": "GET", "message": "This is a GET request"})
|
596
|
+
# elif req.method == 'POST':
|
597
|
+
# data = req.json() # Get JSON data from POST body
|
598
|
+
# return app.json_response({"method": "POST", "received": data})
|
599
|
+
|
600
|
+
# Define route for form submission, rendering form.html for GET and result.html for POST
|
601
|
+
# @app.route('/submit', methods=['GET', 'POST'])
|
602
|
+
# def submit_form(req):
|
603
|
+
# if req.method == 'POST':
|
604
|
+
# return app.render_template('result.html',
|
605
|
+
# data=str(req.form), # Convert form data to string
|
606
|
+
# method="POST")
|
607
|
+
# else:
|
608
|
+
# return app.render_template('form.html')
|
609
|
+
|
610
|
+
# Register static file style.css to be served at /style.css
|
611
|
+
# app.add_static('/style.css', 'style.css')
|
612
|
+
|
613
|
+
# Start the MicroWeb server
|
614
|
+
app.run()
|
615
|
+
"""
|
616
|
+
|
617
|
+
|
618
|
+
index_content = """<!DOCTYPE html>
|
619
|
+
<html>
|
620
|
+
<head>
|
621
|
+
<title>MicroWeb Example</title>
|
622
|
+
</head>
|
623
|
+
<body>
|
624
|
+
<h1>{% message %}</h1>
|
625
|
+
<p>This is an example MicroWeb application.</p>
|
626
|
+
</body>
|
627
|
+
</html>
|
628
|
+
"""
|
629
|
+
|
630
|
+
readme_content = """# MicroWeb Example Application
|
631
|
+
|
632
|
+
This is a simple MicroWeb application for MicroPython devices (e.g., ESP32). It includes a basic web server with a homepage and a status API endpoint.
|
633
|
+
|
634
|
+
## Files
|
635
|
+
- `app.py`: The main MicroWeb application script.
|
636
|
+
- `static/index.html`: The homepage template.
|
637
|
+
|
638
|
+
## Setup and Usage
|
639
|
+
|
640
|
+
### Prerequisites
|
641
|
+
- A MicroPython-compatible device (e.g., ESP32).
|
642
|
+
- The `microweb` package installed (`pip install microweb`).
|
643
|
+
- A serial port connection (e.g., COM10).
|
644
|
+
|
645
|
+
### Flash MicroPython and MicroWeb
|
646
|
+
1. Connect your device to your computer.
|
647
|
+
2. Flash MicroPython and MicroWeb to your device:
|
648
|
+
```bash
|
649
|
+
microweb flash --port COM10
|
650
|
+
```
|
651
|
+
Replace `COM10` with your device's serial port.
|
652
|
+
|
653
|
+
### Run the Application
|
654
|
+
1. Upload and run the application:
|
655
|
+
```bash
|
656
|
+
microweb run app.py --port COM10
|
657
|
+
```
|
658
|
+
2. Connect to the Wi-Fi access point:
|
659
|
+
- **SSID**: MyESP32
|
660
|
+
- **Password**: MyPassword
|
661
|
+
3. Open a browser and visit `http://192.168.4.1` to see the homepage.
|
662
|
+
|
663
|
+
### Additional Commands
|
664
|
+
- Set the app to run on boot:
|
665
|
+
```bash
|
666
|
+
microweb run app.py --port COM10 --add-boot
|
667
|
+
```
|
668
|
+
- Remove all files from the device:
|
669
|
+
```bash
|
670
|
+
microweb remove --port COM10 --remove
|
671
|
+
```
|
672
|
+
|
673
|
+
For more details, run:
|
674
|
+
```bash
|
675
|
+
microweb examples
|
676
|
+
```
|
677
|
+
"""
|
678
|
+
|
679
|
+
app_path = os.path.join(path, 'app.py')
|
680
|
+
index_path = os.path.join(path, 'static', 'index.html')
|
681
|
+
readme_path = os.path.join(path, 'README.md')
|
682
|
+
|
683
|
+
with open(app_path, 'w', encoding='utf-8') as f:
|
684
|
+
f.write(app_content)
|
685
|
+
with open(index_path, 'w', encoding='utf-8') as f:
|
686
|
+
f.write(index_content)
|
687
|
+
with open(readme_path, 'w', encoding='utf-8') as f:
|
688
|
+
f.write(readme_content)
|
689
|
+
|
690
|
+
print_colored(f"Example app created at {path}/", color='green')
|
691
|
+
print_colored(f" - {app_path}", color='cyan')
|
692
|
+
print_colored(f" - {index_path}", color='cyan')
|
693
|
+
print_colored(f" - {readme_path}", color='cyan')
|
694
|
+
print_colored(f"Run with: microweb run {app_path} --port COM10", color='yellow')
|
695
|
+
except Exception as e:
|
696
|
+
print_colored(f"Error creating example app: {e}", color='red')
|
697
|
+
|
698
|
+
|
699
|
+
@cli.command()
|
700
|
+
@click.option('--port', default=None, help='Serial port, e.g., COM10')
|
701
|
+
def ls(port):
|
702
|
+
"""List files on the MicroPython device's filesystem."""
|
703
|
+
if not port:
|
704
|
+
ports = [p.device for p in serial.tools.list_ports.comports()]
|
705
|
+
port = ports[0] if ports else None
|
706
|
+
if not port:
|
707
|
+
print_colored("No device found. Specify --port, e.g., --port COM10.", color='red')
|
708
|
+
return
|
709
|
+
try:
|
710
|
+
print_colored(f"Listing files on device at {port}...", color='blue')
|
711
|
+
remote_files = get_remote_file_info(port)
|
712
|
+
if not remote_files:
|
713
|
+
print_colored("No files found or error accessing filesystem.", color='yellow')
|
714
|
+
return
|
715
|
+
print_colored(f"Found {len(remote_files)} files on device:", color='green')
|
716
|
+
for filename, size in sorted(remote_files.items()):
|
717
|
+
print_colored(f" 📄 {filename}: {size} bytes", color='cyan')
|
718
|
+
except Exception as e:
|
719
|
+
print_colored(f"Error listing files: {e}", color='red')
|
720
|
+
|
721
|
+
|
722
|
+
|
520
723
|
@cli.command()
|
521
724
|
def examples():
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
725
|
+
"""Show example commands for using microweb CLI."""
|
726
|
+
print_colored("Example commands for microweb CLI:", color='blue', style='bold')
|
727
|
+
print_colored("\n1. Create an example MicroWeb app:", color='cyan')
|
728
|
+
print_colored(" microweb create --path example_app", color='green')
|
729
|
+
print_colored("\n2. Flash MicroPython and MicroWeb to ESP32:", color='cyan')
|
730
|
+
print_colored(" microweb flash --port COM10", color='green')
|
731
|
+
print_colored("\n3. Upload and run your app.py on ESP32:", color='cyan')
|
732
|
+
print_colored(" microweb run app.py --port COM10", color='green')
|
733
|
+
print_colored("\n4. Check static/template files without uploading:", color='cyan')
|
734
|
+
print_colored(" microweb run app.py --check-only", color='green')
|
735
|
+
print_colored("\n5. List files on the ESP32 filesystem:", color='cyan')
|
736
|
+
print_colored(" microweb ls --port COM10", color='green')
|
737
|
+
print_colored("\n6. Remove all files from ESP32 (DANGEROUS):", color='cyan')
|
738
|
+
print_colored(" microweb remove --port COM10 --remove", color='green')
|
739
|
+
print_colored("\n7. Upload and set app to run on boot:", color='cyan')
|
740
|
+
print_colored(" microweb run app.py --port COM10 --add-boot", color='green')
|
741
|
+
print_colored("\n8. Remove boot.py from ESP32:", color='cyan')
|
742
|
+
print_colored(" microweb run app.py --port COM10 --remove-boot", color='green')
|
743
|
+
print_colored("\nReplace COM10 with your actual ESP32 serial port.", color='yellow')
|
744
|
+
|
537
745
|
|
538
746
|
|
539
747
|
if __name__ == '__main__':
|
microweb/uploader.py
CHANGED
@@ -33,7 +33,7 @@ def upload_file(file_path, port=None,destination=None):
|
|
33
33
|
raise Exception(f"File {file_name} not found after upload")
|
34
34
|
|
35
35
|
except subprocess.TimeoutExpired:
|
36
|
-
raise Exception(f"Upload timeout for {file_name}")
|
36
|
+
raise Exception(f"Upload timeout for {file_name} or that's uploaded that is effect to boot.py that is why timeout. ")
|
37
37
|
except Exception as e:
|
38
38
|
raise Exception(f"Upload error for {file_name}: {e}")
|
39
39
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: microweb
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.4
|
4
4
|
Summary: A web server framework for MicroPython . Easily build and deploy web applications using MicroPython.
|
5
5
|
Home-page: https://github.com/ishanoshada/microweb
|
6
6
|
Author: Ishan Oshada
|
@@ -15,6 +15,8 @@ License-File: LICENSE
|
|
15
15
|
Requires-Dist: pyserial
|
16
16
|
Requires-Dist: esptool
|
17
17
|
Requires-Dist: click
|
18
|
+
Requires-Dist: adafruit-ampy
|
19
|
+
Requires-Dist: mpremote
|
18
20
|
Dynamic: author
|
19
21
|
Dynamic: classifier
|
20
22
|
Dynamic: description
|
@@ -84,6 +86,7 @@ With MicroWeb, you get routing, templates, JSON, static files, and more—making
|
|
84
86
|
- [Features](#features)
|
85
87
|
- [Installation](#installation)
|
86
88
|
- [Usage](#usage)
|
89
|
+
- [Creating an Example Application](#creating-an-example-application)
|
87
90
|
- [Flashing the ESP32](#flashing-the-esp32)
|
88
91
|
- [Running a Custom Application](#running-a-custom-application)
|
89
92
|
- [Example Usage](#example-usage)
|
@@ -93,6 +96,7 @@ With MicroWeb, you get routing, templates, JSON, static files, and more—making
|
|
93
96
|
- [Wi-Fi Configuration](#wi-fi-configuration)
|
94
97
|
- [Accessing the Web Server](#accessing-the-web-server)
|
95
98
|
- [CLI Tool Usage Examples](#cli-tool-usage-examples)
|
99
|
+
- [How to Code with MicroWeb](#how-to-code-with-microweb)
|
96
100
|
- [ Feature Updates ](#feature-updates)
|
97
101
|
- [Project Structure](#project-structure)
|
98
102
|
- [Troubleshooting](#troubleshooting)
|
@@ -137,32 +141,32 @@ pip install .
|
|
137
141
|
|
138
142
|
---
|
139
143
|
|
144
|
+
|
140
145
|
## Usage
|
141
146
|
|
142
|
-
|
147
|
+
### Creating an Example Application
|
148
|
+
Generate a sample MicroWeb application with a basic web server, template, and documentation:
|
143
149
|
|
144
150
|
```bash
|
145
|
-
microweb
|
151
|
+
microweb create --path example_app
|
146
152
|
```
|
147
153
|
|
148
|
-
|
149
|
-
|
150
|
-
* `--erase`
|
151
|
-
Erase the entire flash memory before flashing firmware.
|
152
|
-
|
153
|
-
* `--esp8266`
|
154
|
-
Flash ESP8266 firmware instead of the default ESP32.
|
155
|
-
|
156
|
-
* `--firmware firmware.bin`
|
157
|
-
Use a custom `.bin` firmware file. This overrides the default firmware for ESP32 or ESP8266 ( any ).
|
158
|
-
|
154
|
+
- Creates a directory (default: `example_app`) containing `app.py`, `static/index.html`, and a `README.md` with usage instructions.
|
155
|
+
- Option: `--path <directory>` to specify a custom directory name.
|
159
156
|
|
157
|
+
### Flashing MicroPython and MicroWeb
|
158
|
+
Flash MicroPython firmware and MicroWeb to your device:
|
160
159
|
|
160
|
+
```bash
|
161
|
+
microweb flash --port COM10
|
162
|
+
```
|
161
163
|
|
162
|
-
|
164
|
+
#### Options:
|
165
|
+
- `--erase`: Erase the entire flash memory before flashing firmware.
|
166
|
+
- `--esp8266`: Flash ESP8266 firmware instead of the default ESP32.
|
167
|
+
- `--firmware firmware.bin`: Use a custom `.bin` firmware file, overriding the default firmware for ESP32 or ESP8266.
|
163
168
|
|
164
169
|
### Running a Custom Application
|
165
|
-
|
166
170
|
Upload and run a MicroPython script:
|
167
171
|
|
168
172
|
```bash
|
@@ -173,7 +177,17 @@ microweb run app.py --port COM10
|
|
173
177
|
- Uploads and executes the script.
|
174
178
|
- Checks and uploads only changed files by default.
|
175
179
|
- Prompts to run `microweb flash` if MicroPython is not detected.
|
176
|
-
- After running `app.run()`, the ESP32 will host a Wi-Fi access point (AP) if it cannot connect to a configured network.
|
180
|
+
- After running `app.run()`, the ESP32 will host a Wi-Fi access point (AP) if it cannot connect to a configured network. Connect to this AP and access the web server at `http://192.168.4.1` (typical IP in AP mode).
|
181
|
+
|
182
|
+
### Listing Files on the Device
|
183
|
+
List files on the MicroPython device's filesystem:
|
184
|
+
|
185
|
+
```bash
|
186
|
+
microweb ls --port COM10
|
187
|
+
```
|
188
|
+
|
189
|
+
- Displays all files and their sizes in the device's home directory.
|
190
|
+
- Requires MicroPython to be installed on the device.
|
177
191
|
|
178
192
|
---
|
179
193
|
|
@@ -319,23 +333,26 @@ If no credentials are provided, loads `config.json`. If connection fails, starts
|
|
319
333
|
- Use the control panel to update Wi-Fi, test routes, or submit forms.
|
320
334
|
|
321
335
|
---
|
336
|
+
|
322
337
|
## CLI Tool Usage Examples
|
323
338
|
|
324
339
|
The following table summarizes common `microweb` CLI commands. See also: #changes.
|
325
340
|
|
326
341
|
| Command Example | Description |
|
327
342
|
|----------------------------------------------|-----------------------------------------------------------|
|
328
|
-
| `microweb
|
343
|
+
| `microweb create --path example_app` | Create an example MicroWeb app with `app.py`, `static/index.html`, and `README.md`. |
|
344
|
+
| `microweb examples` | Show example commands for using the MicroWeb CLI. |
|
329
345
|
| `microweb flash --port COM10` | Flash MicroPython firmware and upload MicroWeb files. |
|
330
346
|
| `microweb flash --port COM10 --erase` | Erase ESP32 flash before installing MicroPython. |
|
331
347
|
| `microweb run app.py --port COM10` | Upload and run a custom MicroPython script. |
|
332
348
|
| `microweb run app.py --check-only` | Check static/template dependencies without uploading. |
|
333
349
|
| `microweb run app.py --force` | Force upload all files, even if unchanged. |
|
334
|
-
| `microweb run app.py --add-boot` |
|
335
|
-
| `microweb run app.py --remove-boot` |
|
350
|
+
| `microweb run app.py --add-boot` | Upload a `boot.py` to auto-run your app on boot. |
|
351
|
+
| `microweb run app.py --remove-boot` | Remove `boot.py` from the ESP32. |
|
336
352
|
| `microweb run app.py --static static/` | Specify a custom static files folder. |
|
337
353
|
| `microweb run app.py --no-stop` | Do not reset ESP32 before running the app. |
|
338
354
|
| `microweb run app.py --timeout 600` | Set a custom timeout (in seconds) for app execution. |
|
355
|
+
| `microweb ls --port COM10` | List files and their sizes on the ESP32 filesystem. |
|
339
356
|
| `microweb remove --port COM10` | List files on ESP32 (requires `--remove` to actually delete). |
|
340
357
|
| `microweb remove --port COM10 --remove` | Remove all files in ESP32 home directory. |
|
341
358
|
|
@@ -347,6 +364,284 @@ The following table summarizes common `microweb` CLI commands. See also: #change
|
|
347
364
|
For more details, run `microweb --help`.
|
348
365
|
|
349
366
|
|
367
|
+
|
368
|
+
---
|
369
|
+
|
370
|
+
## How to Code with MicroWeb
|
371
|
+
|
372
|
+
This section guides you through writing MicroWeb applications for MicroPython on ESP32. MicroWeb simplifies web development with features like dynamic routing, template rendering, static file serving, JSON responses, and Wi-Fi configuration. Below, we explain the key components of coding with MicroWeb, with examples to help you get started.
|
373
|
+
|
374
|
+
### **1. Setting Up the MicroWeb Application**
|
375
|
+
To start, import the `MicroWeb` class and initialize the app. You can configure debugging and Wi-Fi settings (access point or station mode) in the constructor.
|
376
|
+
|
377
|
+
```python
|
378
|
+
from microweb import MicroWeb
|
379
|
+
|
380
|
+
# Initialize MicroWeb with debug mode and access point (AP) settings
|
381
|
+
app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
|
382
|
+
```
|
383
|
+
|
384
|
+
**Explanation**:
|
385
|
+
- `debug=True`: Enables detailed logging for troubleshooting, useful during development.
|
386
|
+
- `ap={'ssid': ..., 'password': ...}`: Configures the ESP32 to create a Wi-Fi access point if it cannot connect to a network. Alternatively, use `internet={'ssid': ..., 'password': ...}` for station mode to connect to an existing Wi-Fi network.
|
387
|
+
- If no Wi-Fi credentials are provided, MicroWeb loads settings from `config.json` or starts a default AP (SSID: `ESP32-MicroWeb`, password: `12345678`).
|
388
|
+
|
389
|
+
### **2. Defining Routes**
|
390
|
+
Routes map URLs to handler functions. Use the `@app.route()` decorator to define endpoints and specify HTTP methods (e.g., GET, POST).
|
391
|
+
|
392
|
+
```python
|
393
|
+
@app.route('/')
|
394
|
+
def home(req):
|
395
|
+
return app.render_template('index.html', message='Welcome to MicroWeb!')
|
396
|
+
```
|
397
|
+
|
398
|
+
**Explanation**:
|
399
|
+
- The `@app.route('/')` decorator maps the root URL (`/`) to the `home` function.
|
400
|
+
- The `req` parameter provides access to request data (e.g., `req.method`, `req.form`, `req.json()`).
|
401
|
+
- `app.render_template` renders an HTML template (`index.html`) with dynamic variables (e.g., `message`).
|
402
|
+
|
403
|
+
For dynamic routing with URL parameters:
|
404
|
+
```python
|
405
|
+
@app.route('/greet/<name>')
|
406
|
+
def greet(req, match):
|
407
|
+
name = match.group(1) if match else 'Anonymous'
|
408
|
+
return {'message': f'Hello, {name}!', 'status': 'success'}
|
409
|
+
```
|
410
|
+
|
411
|
+
**Explanation**:
|
412
|
+
- `/greet/<name>` captures a URL parameter (e.g., `/greet/Alice` sets `name` to `Alice`).
|
413
|
+
- The `match` parameter contains the parsed URL parameters, accessed via `match.group(1)`.
|
414
|
+
|
415
|
+
### **3. Handling HTTP Methods**
|
416
|
+
MicroWeb supports multiple HTTP methods (GET, POST, etc.) for a single route using the `methods` parameter.
|
417
|
+
|
418
|
+
```python
|
419
|
+
@app.route('/api/methods', methods=['GET', 'POST'])
|
420
|
+
def methods(req):
|
421
|
+
if req.method == 'GET':
|
422
|
+
return app.json_response({'method': 'GET', 'message': 'This is a GET request'})
|
423
|
+
elif req.method == 'POST':
|
424
|
+
data = req.json()
|
425
|
+
return app.json_response({'method': 'POST', 'received': data})
|
426
|
+
```
|
427
|
+
|
428
|
+
**Explanation**:
|
429
|
+
- The `methods=['GET', 'POST']` parameter allows the route to handle both GET and POST requests.
|
430
|
+
- `req.method` checks the HTTP method to determine the response.
|
431
|
+
- `req.json()` parses JSON data from the POST request body.
|
432
|
+
- `app.json_response` returns a JSON response with the specified data.
|
433
|
+
|
434
|
+
### **4. Rendering Templates**
|
435
|
+
MicroWeb supports rendering HTML templates with dynamic data, ideal for web interfaces. Templates use a simple syntax (e.g., `{% variable %}`) for dynamic content.
|
436
|
+
|
437
|
+
```python
|
438
|
+
@app.route('/submit', methods=['GET', 'POST'])
|
439
|
+
def submit_form(req):
|
440
|
+
if req.method == 'POST':
|
441
|
+
return app.render_template('result.html', data=str(req.form), method='POST')
|
442
|
+
else:
|
443
|
+
return app.render_template('form.html')
|
444
|
+
```
|
445
|
+
|
446
|
+
**Explanation**:
|
447
|
+
- Templates (e.g., `form.html`, `result.html`) must be in the `static/` directory or a specified folder.
|
448
|
+
- `req.form` accesses form data from POST requests.
|
449
|
+
- `app.render_template` passes variables (e.g., `data`, `method`) to the template for dynamic rendering.
|
450
|
+
|
451
|
+
**Example Template (`static/result.html`)**:
|
452
|
+
```html
|
453
|
+
<!DOCTYPE html>
|
454
|
+
<html>
|
455
|
+
<head>
|
456
|
+
<title>Form Result</title>
|
457
|
+
<link rel="stylesheet" href="/style.css">
|
458
|
+
</head>
|
459
|
+
<body>
|
460
|
+
<p><strong>Method:</strong> {% method %}</p>
|
461
|
+
<p><strong>Data:</strong> {% data %}</p>
|
462
|
+
</body>
|
463
|
+
</html>
|
464
|
+
```
|
465
|
+
|
466
|
+
**Explanation**:
|
467
|
+
- The `{% method %}` and `{% data %}` placeholders are replaced with the `method` and `data` variables passed to `app.render_template`.
|
468
|
+
- The `<link rel="stylesheet" href="/style.css">` tag references a static CSS file, served via `app.add_static('/style.css', 'style.css')`.
|
469
|
+
- Ensure `result.html` and `style.css` are in the `static/` directory and uploaded to the ESP32.
|
470
|
+
|
471
|
+
**Example Form Template (`static/form.html`)**:
|
472
|
+
```html
|
473
|
+
<!DOCTYPE html>
|
474
|
+
<html>
|
475
|
+
<head>
|
476
|
+
<title>Form</title>
|
477
|
+
<link rel="stylesheet" href="/style.css">
|
478
|
+
</head>
|
479
|
+
<body>
|
480
|
+
<form method="POST" action="/submit">
|
481
|
+
<input type="text" name="username" placeholder="Enter your name">
|
482
|
+
<button type="submit">Submit</button>
|
483
|
+
</form>
|
484
|
+
</body>
|
485
|
+
</html>
|
486
|
+
```
|
487
|
+
|
488
|
+
**Explanation**:
|
489
|
+
- The form submits data to `/submit` via POST, which is processed by the `submit_form` route.
|
490
|
+
- The `style.css` file is served as a static file, ensuring consistent styling across templates.
|
491
|
+
|
492
|
+
### **5. Serving Static Files**
|
493
|
+
MicroWeb allows serving static files like CSS, JavaScript, or images to enhance web interfaces.
|
494
|
+
|
495
|
+
```python
|
496
|
+
app.add_static('/style.css', 'style.css')
|
497
|
+
```
|
498
|
+
|
499
|
+
**Explanation**:
|
500
|
+
- `app.add_static` maps a URL path (`/style.css`) to a file in the `static/` directory (`style.css`).
|
501
|
+
- Static files must be uploaded to the ESP32’s filesystem using:
|
502
|
+
```bash
|
503
|
+
microweb run app.py --static static/ --port COM10
|
504
|
+
```
|
505
|
+
- In the `result.html` example, `<link rel="stylesheet" href="/style.css">` loads the CSS file for styling.
|
506
|
+
|
507
|
+
### **6. JSON Responses**
|
508
|
+
MicroWeb simplifies JSON responses for API endpoints.
|
509
|
+
|
510
|
+
```python
|
511
|
+
@app.route('/api/status', methods=['GET'])
|
512
|
+
def status(req):
|
513
|
+
return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
|
514
|
+
```
|
515
|
+
|
516
|
+
**Explanation**:
|
517
|
+
- `app.json_response` formats the dictionary as JSON and sets the `Content-Type` to `application/json`.
|
518
|
+
- The `wifi` module (if available) retrieves the ESP32’s IP address.
|
519
|
+
|
520
|
+
### **7. Custom HTTP Headers**
|
521
|
+
You can set custom headers using the `Response` class.
|
522
|
+
|
523
|
+
```python
|
524
|
+
from microweb import Response
|
525
|
+
|
526
|
+
@app.route('/headers')
|
527
|
+
def headers_example(req):
|
528
|
+
resp = Response('Custom header set!', content_type='text/plain')
|
529
|
+
resp.headers['X-Custom-Header'] = 'Value'
|
530
|
+
return resp
|
531
|
+
```
|
532
|
+
|
533
|
+
**Explanation**:
|
534
|
+
- The `Response` class allows custom content types and headers.
|
535
|
+
- `resp.headers` sets additional HTTP headers for the response.
|
536
|
+
|
537
|
+
### **8. Wi-Fi Integration**
|
538
|
+
MicroWeb handles Wi-Fi configuration, allowing the ESP32 to act as an access point or connect to a network.
|
539
|
+
|
540
|
+
```python
|
541
|
+
import wifi
|
542
|
+
from microweb import MicroWeb
|
543
|
+
|
544
|
+
app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
|
545
|
+
|
546
|
+
@app.route('/api/status', methods=['GET'])
|
547
|
+
def status(req):
|
548
|
+
return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
|
549
|
+
```
|
550
|
+
|
551
|
+
**Explanation**:
|
552
|
+
- The `wifi` module (part of MicroWeb or MicroPython) provides functions like `wifi.get_ip()` to retrieve the device’s IP address.
|
553
|
+
- If Wi-Fi connection fails, the ESP32 starts an access point with the specified `ssid` and `password`.
|
554
|
+
|
555
|
+
### **9. Running the Application**
|
556
|
+
Start the web server with `app.run()`.
|
557
|
+
|
558
|
+
```python
|
559
|
+
if __name__ == '__main__':
|
560
|
+
app.run()
|
561
|
+
```
|
562
|
+
|
563
|
+
**Explanation**:
|
564
|
+
- `app.run()` starts the MicroWeb server, listening for HTTP requests.
|
565
|
+
- Use the CLI to upload and run the script:
|
566
|
+
```bash
|
567
|
+
microweb run app.py --port COM10
|
568
|
+
```
|
569
|
+
|
570
|
+
### **10. Best Practices**
|
571
|
+
- **Error Handling**: Add try-except blocks for `wifi.get_ip()` or `req.form` to handle network or input errors.
|
572
|
+
```python
|
573
|
+
try:
|
574
|
+
ip = wifi.get_ip()
|
575
|
+
except Exception as e:
|
576
|
+
ip = 'N/A'
|
577
|
+
return app.json_response({'status': 'running', 'ip': ip})
|
578
|
+
```
|
579
|
+
- **File Management**: Ensure templates (`index.html`, `form.html`, `result.html`) and static files (`style.css`) exist in the `static/` directory before running.
|
580
|
+
- **Security**: Avoid hardcoding Wi-Fi credentials; use `config.json` or the web interface for configuration.
|
581
|
+
- **Debugging**: Enable `debug=True` during development to log errors and requests.
|
582
|
+
- **Testing**: Test routes with tools like `curl` or a browser (e.g., `http://192.168.4.1/` in AP mode).
|
583
|
+
|
584
|
+
### **11. Example: Putting It All Together**
|
585
|
+
Below is a complete MicroWeb application combining routes, templates, static files, and JSON responses, including the `result.html` template.
|
586
|
+
|
587
|
+
```python
|
588
|
+
import wifi
|
589
|
+
from microweb import MicroWeb
|
590
|
+
|
591
|
+
app = MicroWeb(debug=True, ap={'ssid': 'MyESP32', 'password': 'mypassword'})
|
592
|
+
|
593
|
+
@app.route('/')
|
594
|
+
def home(req):
|
595
|
+
return app.render_template('index.html', message='Welcome to MicroWeb!')
|
596
|
+
|
597
|
+
@app.route('/api/status', methods=['GET'])
|
598
|
+
def status(req):
|
599
|
+
return app.json_response({'status': 'running', 'ip': wifi.get_ip()})
|
600
|
+
|
601
|
+
@app.route('/api/echo', methods=['POST'])
|
602
|
+
def echo(req):
|
603
|
+
data = req.form
|
604
|
+
return app.json_response({'received': data})
|
605
|
+
|
606
|
+
@app.route('/submit', methods=['GET', 'POST'])
|
607
|
+
def submit_form(req):
|
608
|
+
if req.method == 'POST':
|
609
|
+
return app.render_template('result.html', data=str(req.form), method='POST')
|
610
|
+
else:
|
611
|
+
return app.render_template('form.html')
|
612
|
+
|
613
|
+
app.add_static('/style.css', 'style.css')
|
614
|
+
|
615
|
+
if __name__ == '__main__':
|
616
|
+
app.run()
|
617
|
+
```
|
618
|
+
|
619
|
+
**Explanation**:
|
620
|
+
- Combines template rendering (`/`, `/submit`), JSON responses (`/api/status`, `/api/echo`), and static file serving (`/style.css`).
|
621
|
+
- The `/submit` route uses `result.html` (as shown above) to display form data and the HTTP method.
|
622
|
+
- Upload and run with:
|
623
|
+
```bash
|
624
|
+
microweb run app.py --port COM10 --static static/
|
625
|
+
```
|
626
|
+
- Access at `http://192.168.4.1` (or the ESP32’s IP address).
|
627
|
+
|
628
|
+
### **12. Testing Your Application**
|
629
|
+
- **Browser**: Open `http://<ESP32-IP>/` (e.g., `http://192.168.4.1`) to access the web server.
|
630
|
+
- **curl**:
|
631
|
+
```bash
|
632
|
+
curl http://192.168.4.1/api/status
|
633
|
+
curl -X POST -d 'username=Alice' http://192.168.4.1/api/echo
|
634
|
+
curl -X POST -d 'username=Alice' http://192.168.4.1/submit
|
635
|
+
```
|
636
|
+
- **CLI Logs**: Monitor logs with `debug=True` to debug issues.
|
637
|
+
- **Template Testing**: Submit a form via `http://192.168.4.1/submit` to see the `result.html` output, styled with `style.css`.
|
638
|
+
|
639
|
+
### **13. Next Steps**
|
640
|
+
- Explore the portfolio demo (`tests/portfolio/`) for a full-featured web app example.
|
641
|
+
- Use `microweb create --path my_app` to generate a template project.
|
642
|
+
- Add boot script support with `microweb run app.py --add-boot --port COM10` for auto-running on power-up.
|
643
|
+
- Check the [CLI Tool Usage Examples](#cli-tool-usage-examples) for advanced commands.
|
644
|
+
|
350
645
|
---
|
351
646
|
|
352
647
|
### Feature Updates
|
@@ -388,6 +683,8 @@ microweb/
|
|
388
683
|
```
|
389
684
|
|
390
685
|
|
686
|
+
|
687
|
+
|
391
688
|
---
|
392
689
|
|
393
690
|
## Troubleshooting
|
@@ -1,15 +1,15 @@
|
|
1
1
|
microweb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
microweb/cli.py,sha256=
|
2
|
+
microweb/cli.py,sha256=Btvegc_SJ76rLkSvItuiWLySRDrNy2b0gdUrg2a-zKo,34338
|
3
3
|
microweb/microweb.py,sha256=WlDV1Fk5M4EsvmmkWKVLWaoZh6Vgl9G0qORFsQEQ3iM,12799
|
4
|
-
microweb/uploader.py,sha256=
|
4
|
+
microweb/uploader.py,sha256=4aTFBMwIIKzONuvXla70ch7yT1pJ8XDmInYBPfVqcok,3164
|
5
5
|
microweb/wifi.py,sha256=S2gOOmTKp2G1uUHxOH0DHQ_k7QSERKHDCm89qv-WlKs,904
|
6
6
|
microweb/firmware/ESP32_GENERIC-20250415-v1.25.0.bin,sha256=HrGohHPjKqak1F3BtbatQLwfBhKOeqhUjkOM-kYz2hs,1702240
|
7
7
|
microweb/firmware/ESP8266_GENERIC-20250415-v1.25.0.bin,sha256=DcEkzHgtDl-BCwap6UbgKhirHjbWepSgd8PonauaVcY,636820
|
8
8
|
microweb/firmware/boot.py,sha256=1522jvxAjGk-y7X4mzMilB4pMQ6KrAgosDXRAMtac8k,22
|
9
9
|
microweb/firmware/main.py,sha256=g_rg-1qpQEzvQoSnhXairx_v7xHNuGDJVrmbdziKt1A,10
|
10
|
-
microweb-0.1.
|
11
|
-
microweb-0.1.
|
12
|
-
microweb-0.1.
|
13
|
-
microweb-0.1.
|
14
|
-
microweb-0.1.
|
15
|
-
microweb-0.1.
|
10
|
+
microweb-0.1.4.dist-info/licenses/LICENSE,sha256=7tSPQeJFv8168MWaFKTWEqYRvM9Lh5xsBh-US0mxUF0,1070
|
11
|
+
microweb-0.1.4.dist-info/METADATA,sha256=wHRZhYMs8_sAMmNzz3ewkEpcm8gSqcvibAmSvVGPJDc,25314
|
12
|
+
microweb-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
+
microweb-0.1.4.dist-info/entry_points.txt,sha256=yBGjIy-e3mx-HHkmkGNLBcSDEALB327du5KuauZHmg4,46
|
14
|
+
microweb-0.1.4.dist-info/top_level.txt,sha256=CkRcDTo-nv4JTieAZPKFC-EGKYYQmKNq5C8zU2k5zRM,9
|
15
|
+
microweb-0.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|