|
3 | 3 | from esp32 import Partition |
4 | 4 | import urequests |
5 | 5 |
|
| 6 | +import lvgl as lv |
| 7 | +from esp32 import Partition |
| 8 | +import urequests |
| 9 | +import hashlib |
| 10 | +import machine |
6 | 11 |
|
7 | 12 | subwindow.clean() |
8 | 13 | canary = lv.obj(subwindow) |
|
15 | 20 | current |
16 | 21 | current.get_next_update() |
17 | 22 |
|
18 | | -# Initialize LVGL display (assuming setup is done) |
| 23 | + |
| 24 | +# Assuming subwindow is defined elsewhere |
| 25 | +# subwindow.clean() |
| 26 | + |
| 27 | +# Initialize LVGL display |
19 | 28 | label = lv.label(subwindow) |
20 | 29 | label.set_text("OTA Update: 0.00%") |
21 | | -label.align(lv.ALIGN.CENTER, 0, -30) |
22 | | -progress_bar = lv.bar(subwindow) |
| 30 | +label.align(lv.ALIGN.CENTER, 0, 0) |
| 31 | +progress_bar = lv.bar(lv.screen_active()) |
23 | 32 | progress_bar.set_size(200, 20) |
24 | | -progress_bar.align(lv.ALIGN.BOTTOM_MID, 0, -50) |
| 33 | +progress_bar.align(lv.ALIGN.BOTTOM_MID, 0, -20) |
25 | 34 | progress_bar.set_range(0, 100) |
26 | 35 | progress_bar.set_value(0, lv.ANIM.OFF) |
27 | 36 |
|
28 | 37 | # Custom OTA update with LVGL progress |
29 | | -def update_with_lvgl(url): |
| 38 | +def update_with_lvgl(url, expected_sha256=None): |
30 | 39 | def progress_callback(percent): |
31 | 40 | print(f"OTA Update: {percent:.1f}%") |
32 | | - label.set_text(f"OTA Update: {percent:.2f}%") # Cloud upload symbol |
| 41 | + label.set_text(f"OTA Update: {percent:.2f}%") |
33 | 42 | progress_bar.set_value(int(percent), lv.ANIM.ON) |
| 43 | + lv.task_handler() # Ensure LVGL updates the display |
| 44 | + |
| 45 | + # Free memory before starting |
| 46 | + import gc |
| 47 | + gc.collect() |
| 48 | + |
| 49 | + # Get partition information |
34 | 50 | current = Partition(Partition.RUNNING) |
35 | 51 | next_partition = current.get_next_update() |
| 52 | + print("Current partition:", current.info()) |
| 53 | + print("Next partition:", next_partition.info()) |
| 54 | + |
| 55 | + # Validate partition size |
| 56 | + partition_size = next_partition.ioctl(4, 0) * 4096 # Total sectors * sector size |
36 | 57 | response = urequests.get(url, stream=True) |
37 | 58 | total_size = int(response.headers.get('Content-Length', 0)) |
| 59 | + if total_size > partition_size: |
| 60 | + print(f"Error: Firmware size {total_size} exceeds partition size {partition_size}") |
| 61 | + response.close() |
| 62 | + return |
| 63 | + print(f"Starting OTA update of size: {total_size}") |
| 64 | + |
| 65 | + # Initialize SHA256 hash |
| 66 | + sha256 = hashlib.sha256() if expected_sha256 else None |
| 67 | + |
38 | 68 | bytes_written = 0 |
39 | 69 | chunk_size = 4096 |
40 | 70 | i = 0 |
41 | | - print(f"Starting OTA update of size: {total_size}") |
42 | | - while canary.is_valid(): |
| 71 | + while True: |
43 | 72 | chunk = response.raw.read(chunk_size) |
44 | 73 | if not chunk: |
45 | | - print("No chunk, breaking...") |
46 | 74 | break |
| 75 | + print(f"Writing chunk {i}, size: {len(chunk)} bytes") |
| 76 | + # Pad the chunk if it's smaller than 4096 bytes |
47 | 77 | if len(chunk) < chunk_size: |
48 | 78 | print(f"Padding chunk {i} from {len(chunk)} to {chunk_size} bytes") |
49 | 79 | chunk = chunk + b'\xFF' * (chunk_size - len(chunk)) |
50 | | - print(f"Writing chunk {i}") |
51 | | - next_partition.writeblocks(i, chunk) |
| 80 | + # Update hash |
| 81 | + if sha256: |
| 82 | + sha256.update(chunk) |
| 83 | + try: |
| 84 | + next_partition.writeblocks(i, chunk) |
| 85 | + except OSError as e: |
| 86 | + print(f"Error writing chunk {i}: {e}") |
| 87 | + response.close() |
| 88 | + return |
52 | 89 | bytes_written += len(chunk) |
53 | 90 | i += 1 |
54 | 91 | if total_size: |
55 | 92 | progress_callback(bytes_written / total_size * 100) |
| 93 | + |
56 | 94 | response.close() |
57 | | - next_partition.set_boot() |
58 | | - import machine |
| 95 | + print(f"OTA update complete, wrote {bytes_written} bytes") |
| 96 | + |
| 97 | + # Verify checksum if provided |
| 98 | + if sha256 and expected_sha256: |
| 99 | + computed_sha256 = sha256.hexdigest() |
| 100 | + if computed_sha256 != expected_sha256: |
| 101 | + print(f"Checksum mismatch: expected {expected_sha256}, got {computed_sha256}") |
| 102 | + return |
| 103 | + print("Checksum verified successfully") |
| 104 | + |
| 105 | + # Set the boot partition |
| 106 | + try: |
| 107 | + next_partition.set_boot() |
| 108 | + print("Boot partition set to:", next_partition.info()) |
| 109 | + print("Current boot partition:", Partition(Partition.BOOT).info()) |
| 110 | + except OSError as e: |
| 111 | + print(f"Error setting boot partition: {e}") |
| 112 | + return |
| 113 | + |
| 114 | + # Mark the new partition as valid to prevent rollback |
| 115 | + try: |
| 116 | + Partition.mark_app_valid_cancel_rollback() |
| 117 | + print("Marked new partition as valid") |
| 118 | + except OSError as e: |
| 119 | + print(f"Error marking partition as valid: {e}") |
| 120 | + return |
| 121 | + |
| 122 | + # Reboot |
| 123 | + print("Rebooting to new firmware...") |
59 | 124 | machine.reset() |
60 | 125 |
|
| 126 | +# Example usage |
| 127 | +# Replace with your firmware URL and SHA256 checksum (if available) |
| 128 | +update_with_lvgl("http://demo.lnpiggy.com:2121/ESP32_GENERIC_S3-SPIRAM_OCT_micropython.bin", expected_sha256="c167de508c354724e97857ed4bd5c5608f383302399a506d2a16d6ee0cab08ce") |
| 129 | +lvgl_micropy_ESP32_GENERIC_S3-SPIRAM_OCT-16.bin |
| 130 | + |
61 | 131 | # Start OTA update |
62 | | -update_with_lvgl("http://demo.lnpiggy.com:2121/ESP32_GENERIC_S3-SPIRAM_OCT_micropython.bin") |
| 132 | +#update_with_lvgl("http://demo.lnpiggy.com:2121/latest.bin") |
0 commit comments