mpytool 2.2.2__tar.gz → 2.2.4__tar.gz
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.
- {mpytool-2.2.2 → mpytool-2.2.4}/PKG-INFO +52 -23
- {mpytool-2.2.2 → mpytool-2.2.4}/README.md +51 -22
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/mpy.py +28 -6
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/mpy_comm.py +18 -7
- mpytool-2.2.4/mpytool/mpy_cross.py +155 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/mpytool.py +95 -48
- mpytool-2.2.4/mpytool/speedtest.py +121 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/utils.py +15 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/PKG-INFO +52 -23
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/SOURCES.txt +2 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/pyproject.toml +1 -1
- {mpytool-2.2.2 → mpytool-2.2.4}/tests/test_integration.py +92 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/tests/test_mpy.py +78 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/tests/test_mpytool.py +304 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/LICENSE +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/__init__.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/conn.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/conn_serial.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/conn_socket.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/logger.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/terminal.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/terminal_unix.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool/terminal_win.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/dependency_links.txt +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/entry_points.txt +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/requires.txt +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/mpytool.egg-info/top_level.txt +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/setup.cfg +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/tests/test_errors.py +0 -0
- {mpytool-2.2.2 → mpytool-2.2.4}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mpytool
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: MPY tool - manage files on devices running MicroPython
|
|
5
5
|
Author-email: Pavel Revak <pavel.revak@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -74,25 +74,25 @@ Then restart your shell (`exec zsh` or `exec bash`) and use `mpytool` directly.
|
|
|
74
74
|
|
|
75
75
|
Adding venv bin at the end of PATH keeps your system `python` and `pip` as default, while making `mpytool` available when not found elsewhere.
|
|
76
76
|
|
|
77
|
-
## Examples
|
|
77
|
+
## Examples
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
### Help
|
|
80
80
|
```
|
|
81
81
|
$ mpytool --help
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
### List files
|
|
85
85
|
```
|
|
86
86
|
$ mpytool -p /dev/ttyACM0 ls # list CWD (default)
|
|
87
87
|
$ mpytool -p /dev/ttyACM0 ls :/lib # list /lib
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
### Tree
|
|
91
91
|
```
|
|
92
92
|
$ mpytool -p /dev/ttyACM0 tree # tree of CWD (default)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
### Copy files (: prefix = device path)
|
|
96
96
|
```
|
|
97
97
|
$ mpytool cp main.py :/ # upload file to device root
|
|
98
98
|
$ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
|
|
@@ -103,36 +103,46 @@ $ mpytool cp :/main.py ./ # download file to current directory
|
|
|
103
103
|
$ mpytool cp :/ ./backup/ # download entire device to backup/
|
|
104
104
|
$ mpytool cp :/old.py :/new.py # copy file on device
|
|
105
105
|
$ mpytool cp -f main.py :/ # force upload even if unchanged
|
|
106
|
+
$ mpytool cp -m myapp :/ # compile .py to .mpy via mpy-cross
|
|
106
107
|
```
|
|
107
108
|
|
|
108
109
|
Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
|
|
109
110
|
Unchanged files are automatically skipped (compares size and SHA256 hash).
|
|
110
111
|
Use `-f` or `--force` to upload all files regardless.
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
### Transfer options
|
|
113
114
|
```
|
|
114
115
|
$ mpytool cp -z main.py :/ # force compression (auto-detected by default)
|
|
115
116
|
$ mpytool cp --no-compress data.bin :/ # disable compression
|
|
116
117
|
$ mpytool -c 8K cp main.py :/ # set chunk size (512, 1K, 2K, 4K, 8K, 16K, 32K)
|
|
118
|
+
$ mpytool cp -m myapp :/ # compile .py to .mpy before upload
|
|
119
|
+
$ mpytool cp -fm myapp :/ # force upload + compile .mpy
|
|
117
120
|
```
|
|
118
121
|
|
|
119
122
|
Compression is auto-detected based on device RAM and deflate module availability.
|
|
120
123
|
Chunk size is auto-detected based on free RAM (larger chunks = faster transfer).
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
The `-m`/`--mpy` flag compiles `.py` files to `.mpy` bytecode using `mpy-cross` before upload.
|
|
126
|
+
Compiled files are cached in `__pycache__/` (recompiled only when source changes).
|
|
127
|
+
`boot.py` and `main.py` are always uploaded as `.py` (firmware requirement).
|
|
128
|
+
Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version differs
|
|
129
|
+
from device, automatically uses `-b` flag to target the correct bytecode version.
|
|
130
|
+
If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
|
|
131
|
+
|
|
132
|
+
### Move/rename on device
|
|
123
133
|
```
|
|
124
134
|
$ mpytool mv :/old.py :/new.py # rename file
|
|
125
135
|
$ mpytool mv :/file.py :/lib/ # move file to directory
|
|
126
136
|
$ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
|
|
127
137
|
```
|
|
128
138
|
|
|
129
|
-
|
|
139
|
+
### View file contents
|
|
130
140
|
```
|
|
131
141
|
$ mpytool cat :boot.py # print file from CWD
|
|
132
142
|
$ mpytool cat :/lib/module.py # print file with absolute path
|
|
133
143
|
```
|
|
134
144
|
|
|
135
|
-
|
|
145
|
+
### Make directory, delete files (: prefix = device path)
|
|
136
146
|
```
|
|
137
147
|
$ mpytool mkdir :lib :data # create directories in CWD
|
|
138
148
|
$ mpytool mkdir :/lib/subdir # create with absolute path
|
|
@@ -143,7 +153,7 @@ $ mpytool rm : # delete everything in CWD
|
|
|
143
153
|
$ mpytool rm :/ # delete everything on device (root)
|
|
144
154
|
```
|
|
145
155
|
|
|
146
|
-
|
|
156
|
+
### Current working directory
|
|
147
157
|
```
|
|
148
158
|
$ mpytool pwd # print current directory
|
|
149
159
|
/
|
|
@@ -153,7 +163,7 @@ $ mpytool cd :.. # change to parent directory
|
|
|
153
163
|
$ mpytool cd :/lib -- ls # change directory and list files
|
|
154
164
|
```
|
|
155
165
|
|
|
156
|
-
|
|
166
|
+
### Reset and REPL
|
|
157
167
|
```
|
|
158
168
|
$ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
|
|
159
169
|
$ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
|
|
@@ -167,7 +177,7 @@ $ mpytool repl # enter REPL mode
|
|
|
167
177
|
$ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
168
178
|
```
|
|
169
179
|
|
|
170
|
-
|
|
180
|
+
### Serial terminal and monitor (general purpose)
|
|
171
181
|
```
|
|
172
182
|
$ mpytool repl # auto-detect port, 115200 baud
|
|
173
183
|
$ mpytool -p /dev/ttyUSB0 repl # specify port
|
|
@@ -177,13 +187,32 @@ $ mpytool -p /dev/ttyUSB0 -b 9600 monitor # monitor at 9600 baud
|
|
|
177
187
|
|
|
178
188
|
Both `repl` and `monitor` can be used as general-purpose serial tools - not just for MicroPython devices. Use them to interact with any serial device (Arduino, ESP with custom firmware, GPS modules, etc.). When only one serial port is detected, it is used automatically. Default baudrate is 115200.
|
|
179
189
|
|
|
180
|
-
|
|
190
|
+
### Execute Python code on device
|
|
181
191
|
```
|
|
182
192
|
$ mpytool exec "print('Hello!')"
|
|
183
193
|
$ mpytool exec "import sys; print(sys.version)"
|
|
184
194
|
```
|
|
185
195
|
|
|
186
|
-
|
|
196
|
+
### Run local Python file on device
|
|
197
|
+
```
|
|
198
|
+
$ mpytool run script.py # run script (fire-and-forget)
|
|
199
|
+
$ mpytool run script.py -- monitor # run script and capture output
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Serial link speed test
|
|
203
|
+
```
|
|
204
|
+
$ mpytool speedtest
|
|
205
|
+
SPEEDTEST
|
|
206
|
+
upload download verify
|
|
207
|
+
0B - - ok
|
|
208
|
+
16B 11.8K/s 24.4K/s ok
|
|
209
|
+
32B 13.1K/s 42.6K/s ok
|
|
210
|
+
...
|
|
211
|
+
4.00K 120K/s 11.1K/s ok
|
|
212
|
+
8.00K 157K/s 11.0K/s ok
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Show device information
|
|
187
216
|
```
|
|
188
217
|
$ mpytool info
|
|
189
218
|
Platform: rp2
|
|
@@ -201,7 +230,7 @@ MAC WiFi: aa:bb:cc:dd:ee:01
|
|
|
201
230
|
MAC WiFi AP: aa:bb:cc:dd:ee:02
|
|
202
231
|
```
|
|
203
232
|
|
|
204
|
-
|
|
233
|
+
### Flash operations (RP2 and ESP32)
|
|
205
234
|
```
|
|
206
235
|
# RP2 - user flash (entire filesystem area)
|
|
207
236
|
$ mpytool flash # show flash info and filesystem type
|
|
@@ -227,20 +256,20 @@ $ mpytool flash erase vfs # quick erase partition
|
|
|
227
256
|
$ mpytool flash erase vfs --full # full erase partition
|
|
228
257
|
```
|
|
229
258
|
|
|
230
|
-
OTA firmware update (ESP32)
|
|
259
|
+
### OTA firmware update (ESP32)
|
|
231
260
|
```
|
|
232
261
|
$ mpytool ota firmware.app-bin # flash to next OTA partition
|
|
233
262
|
$ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
|
|
234
263
|
$ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
|
|
235
264
|
```
|
|
236
265
|
|
|
237
|
-
|
|
266
|
+
### Multiple commands separated by `--`
|
|
238
267
|
```
|
|
239
268
|
$ mpytool cp main.py boot.py : -- reset -- monitor
|
|
240
269
|
$ mpytool rm :old.py -- cp new.py : -- reset
|
|
241
270
|
```
|
|
242
271
|
|
|
243
|
-
|
|
272
|
+
### Auto-detect serial port (if only one device is connected)
|
|
244
273
|
```
|
|
245
274
|
$ mpytool ls lib/
|
|
246
275
|
uhttp/
|
|
@@ -248,7 +277,7 @@ $ mpytool ls lib/
|
|
|
248
277
|
4.95 KB wlan_http.py
|
|
249
278
|
```
|
|
250
279
|
|
|
251
|
-
|
|
280
|
+
### Tree view
|
|
252
281
|
```
|
|
253
282
|
$ mpytool tree
|
|
254
283
|
142 KB ./
|
|
@@ -266,18 +295,18 @@ $ mpytool tree
|
|
|
266
295
|
3.03 KB └─ main.py
|
|
267
296
|
```
|
|
268
297
|
|
|
269
|
-
|
|
298
|
+
### Connect over network (TCP, default port 23)
|
|
270
299
|
```
|
|
271
300
|
$ mpytool -a 192.168.1.100 ls
|
|
272
301
|
$ mpytool -a 192.168.1.100:8266 tree
|
|
273
302
|
```
|
|
274
303
|
|
|
275
|
-
|
|
304
|
+
### Set baudrate (default 115200)
|
|
276
305
|
```
|
|
277
306
|
$ mpytool -p /dev/ttyACM0 -b 9600 ls
|
|
278
307
|
```
|
|
279
308
|
|
|
280
|
-
|
|
309
|
+
### Show version
|
|
281
310
|
```
|
|
282
311
|
$ mpytool -V
|
|
283
312
|
```
|
|
@@ -60,25 +60,25 @@ Then restart your shell (`exec zsh` or `exec bash`) and use `mpytool` directly.
|
|
|
60
60
|
|
|
61
61
|
Adding venv bin at the end of PATH keeps your system `python` and `pip` as default, while making `mpytool` available when not found elsewhere.
|
|
62
62
|
|
|
63
|
-
## Examples
|
|
63
|
+
## Examples
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
### Help
|
|
66
66
|
```
|
|
67
67
|
$ mpytool --help
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
### List files
|
|
71
71
|
```
|
|
72
72
|
$ mpytool -p /dev/ttyACM0 ls # list CWD (default)
|
|
73
73
|
$ mpytool -p /dev/ttyACM0 ls :/lib # list /lib
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
### Tree
|
|
77
77
|
```
|
|
78
78
|
$ mpytool -p /dev/ttyACM0 tree # tree of CWD (default)
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
### Copy files (: prefix = device path)
|
|
82
82
|
```
|
|
83
83
|
$ mpytool cp main.py :/ # upload file to device root
|
|
84
84
|
$ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
|
|
@@ -89,36 +89,46 @@ $ mpytool cp :/main.py ./ # download file to current directory
|
|
|
89
89
|
$ mpytool cp :/ ./backup/ # download entire device to backup/
|
|
90
90
|
$ mpytool cp :/old.py :/new.py # copy file on device
|
|
91
91
|
$ mpytool cp -f main.py :/ # force upload even if unchanged
|
|
92
|
+
$ mpytool cp -m myapp :/ # compile .py to .mpy via mpy-cross
|
|
92
93
|
```
|
|
93
94
|
|
|
94
95
|
Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
|
|
95
96
|
Unchanged files are automatically skipped (compares size and SHA256 hash).
|
|
96
97
|
Use `-f` or `--force` to upload all files regardless.
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
### Transfer options
|
|
99
100
|
```
|
|
100
101
|
$ mpytool cp -z main.py :/ # force compression (auto-detected by default)
|
|
101
102
|
$ mpytool cp --no-compress data.bin :/ # disable compression
|
|
102
103
|
$ mpytool -c 8K cp main.py :/ # set chunk size (512, 1K, 2K, 4K, 8K, 16K, 32K)
|
|
104
|
+
$ mpytool cp -m myapp :/ # compile .py to .mpy before upload
|
|
105
|
+
$ mpytool cp -fm myapp :/ # force upload + compile .mpy
|
|
103
106
|
```
|
|
104
107
|
|
|
105
108
|
Compression is auto-detected based on device RAM and deflate module availability.
|
|
106
109
|
Chunk size is auto-detected based on free RAM (larger chunks = faster transfer).
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
The `-m`/`--mpy` flag compiles `.py` files to `.mpy` bytecode using `mpy-cross` before upload.
|
|
112
|
+
Compiled files are cached in `__pycache__/` (recompiled only when source changes).
|
|
113
|
+
`boot.py` and `main.py` are always uploaded as `.py` (firmware requirement).
|
|
114
|
+
Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version differs
|
|
115
|
+
from device, automatically uses `-b` flag to target the correct bytecode version.
|
|
116
|
+
If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
|
|
117
|
+
|
|
118
|
+
### Move/rename on device
|
|
109
119
|
```
|
|
110
120
|
$ mpytool mv :/old.py :/new.py # rename file
|
|
111
121
|
$ mpytool mv :/file.py :/lib/ # move file to directory
|
|
112
122
|
$ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
|
|
113
123
|
```
|
|
114
124
|
|
|
115
|
-
|
|
125
|
+
### View file contents
|
|
116
126
|
```
|
|
117
127
|
$ mpytool cat :boot.py # print file from CWD
|
|
118
128
|
$ mpytool cat :/lib/module.py # print file with absolute path
|
|
119
129
|
```
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
### Make directory, delete files (: prefix = device path)
|
|
122
132
|
```
|
|
123
133
|
$ mpytool mkdir :lib :data # create directories in CWD
|
|
124
134
|
$ mpytool mkdir :/lib/subdir # create with absolute path
|
|
@@ -129,7 +139,7 @@ $ mpytool rm : # delete everything in CWD
|
|
|
129
139
|
$ mpytool rm :/ # delete everything on device (root)
|
|
130
140
|
```
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
### Current working directory
|
|
133
143
|
```
|
|
134
144
|
$ mpytool pwd # print current directory
|
|
135
145
|
/
|
|
@@ -139,7 +149,7 @@ $ mpytool cd :.. # change to parent directory
|
|
|
139
149
|
$ mpytool cd :/lib -- ls # change directory and list files
|
|
140
150
|
```
|
|
141
151
|
|
|
142
|
-
|
|
152
|
+
### Reset and REPL
|
|
143
153
|
```
|
|
144
154
|
$ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
|
|
145
155
|
$ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
|
|
@@ -153,7 +163,7 @@ $ mpytool repl # enter REPL mode
|
|
|
153
163
|
$ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
154
164
|
```
|
|
155
165
|
|
|
156
|
-
|
|
166
|
+
### Serial terminal and monitor (general purpose)
|
|
157
167
|
```
|
|
158
168
|
$ mpytool repl # auto-detect port, 115200 baud
|
|
159
169
|
$ mpytool -p /dev/ttyUSB0 repl # specify port
|
|
@@ -163,13 +173,32 @@ $ mpytool -p /dev/ttyUSB0 -b 9600 monitor # monitor at 9600 baud
|
|
|
163
173
|
|
|
164
174
|
Both `repl` and `monitor` can be used as general-purpose serial tools - not just for MicroPython devices. Use them to interact with any serial device (Arduino, ESP with custom firmware, GPS modules, etc.). When only one serial port is detected, it is used automatically. Default baudrate is 115200.
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
### Execute Python code on device
|
|
167
177
|
```
|
|
168
178
|
$ mpytool exec "print('Hello!')"
|
|
169
179
|
$ mpytool exec "import sys; print(sys.version)"
|
|
170
180
|
```
|
|
171
181
|
|
|
172
|
-
|
|
182
|
+
### Run local Python file on device
|
|
183
|
+
```
|
|
184
|
+
$ mpytool run script.py # run script (fire-and-forget)
|
|
185
|
+
$ mpytool run script.py -- monitor # run script and capture output
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Serial link speed test
|
|
189
|
+
```
|
|
190
|
+
$ mpytool speedtest
|
|
191
|
+
SPEEDTEST
|
|
192
|
+
upload download verify
|
|
193
|
+
0B - - ok
|
|
194
|
+
16B 11.8K/s 24.4K/s ok
|
|
195
|
+
32B 13.1K/s 42.6K/s ok
|
|
196
|
+
...
|
|
197
|
+
4.00K 120K/s 11.1K/s ok
|
|
198
|
+
8.00K 157K/s 11.0K/s ok
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Show device information
|
|
173
202
|
```
|
|
174
203
|
$ mpytool info
|
|
175
204
|
Platform: rp2
|
|
@@ -187,7 +216,7 @@ MAC WiFi: aa:bb:cc:dd:ee:01
|
|
|
187
216
|
MAC WiFi AP: aa:bb:cc:dd:ee:02
|
|
188
217
|
```
|
|
189
218
|
|
|
190
|
-
|
|
219
|
+
### Flash operations (RP2 and ESP32)
|
|
191
220
|
```
|
|
192
221
|
# RP2 - user flash (entire filesystem area)
|
|
193
222
|
$ mpytool flash # show flash info and filesystem type
|
|
@@ -213,20 +242,20 @@ $ mpytool flash erase vfs # quick erase partition
|
|
|
213
242
|
$ mpytool flash erase vfs --full # full erase partition
|
|
214
243
|
```
|
|
215
244
|
|
|
216
|
-
OTA firmware update (ESP32)
|
|
245
|
+
### OTA firmware update (ESP32)
|
|
217
246
|
```
|
|
218
247
|
$ mpytool ota firmware.app-bin # flash to next OTA partition
|
|
219
248
|
$ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
|
|
220
249
|
$ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
|
|
221
250
|
```
|
|
222
251
|
|
|
223
|
-
|
|
252
|
+
### Multiple commands separated by `--`
|
|
224
253
|
```
|
|
225
254
|
$ mpytool cp main.py boot.py : -- reset -- monitor
|
|
226
255
|
$ mpytool rm :old.py -- cp new.py : -- reset
|
|
227
256
|
```
|
|
228
257
|
|
|
229
|
-
|
|
258
|
+
### Auto-detect serial port (if only one device is connected)
|
|
230
259
|
```
|
|
231
260
|
$ mpytool ls lib/
|
|
232
261
|
uhttp/
|
|
@@ -234,7 +263,7 @@ $ mpytool ls lib/
|
|
|
234
263
|
4.95 KB wlan_http.py
|
|
235
264
|
```
|
|
236
265
|
|
|
237
|
-
|
|
266
|
+
### Tree view
|
|
238
267
|
```
|
|
239
268
|
$ mpytool tree
|
|
240
269
|
142 KB ./
|
|
@@ -252,18 +281,18 @@ $ mpytool tree
|
|
|
252
281
|
3.03 KB └─ main.py
|
|
253
282
|
```
|
|
254
283
|
|
|
255
|
-
|
|
284
|
+
### Connect over network (TCP, default port 23)
|
|
256
285
|
```
|
|
257
286
|
$ mpytool -a 192.168.1.100 ls
|
|
258
287
|
$ mpytool -a 192.168.1.100:8266 tree
|
|
259
288
|
```
|
|
260
289
|
|
|
261
|
-
|
|
290
|
+
### Set baudrate (default 115200)
|
|
262
291
|
```
|
|
263
292
|
$ mpytool -p /dev/ttyACM0 -b 9600 ls
|
|
264
293
|
```
|
|
265
294
|
|
|
266
|
-
|
|
295
|
+
### Show version
|
|
267
296
|
```
|
|
268
297
|
$ mpytool -V
|
|
269
298
|
```
|
|
@@ -5,6 +5,9 @@ import zlib
|
|
|
5
5
|
|
|
6
6
|
import mpytool.mpy_comm as _mpy_comm
|
|
7
7
|
|
|
8
|
+
# Timeout for flash I/O operations (open/close/gc) - some FLASH GC can block >5s
|
|
9
|
+
FLASH_IO_TIMEOUT = 20
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
def _escape_path(path: str) -> str:
|
|
10
13
|
"""Escape path for use in Python string literal"""
|
|
@@ -515,7 +518,7 @@ def _mt_pfind(label):
|
|
|
515
518
|
self.import_module('deflate as df')
|
|
516
519
|
self.import_module('io as _io')
|
|
517
520
|
|
|
518
|
-
self._mpy_comm.exec(f"f = open('{_escape_path(path)}', 'wb')")
|
|
521
|
+
self._mpy_comm.exec(f"f = open('{_escape_path(path)}', 'wb')", timeout=FLASH_IO_TIMEOUT)
|
|
519
522
|
while data:
|
|
520
523
|
chunk = data[:chunk_size]
|
|
521
524
|
cmd, orig_size, enc_type = self._encode_chunk(chunk, compress)
|
|
@@ -527,10 +530,10 @@ def _mt_pfind(label):
|
|
|
527
530
|
transferred += orig_size
|
|
528
531
|
if progress_callback:
|
|
529
532
|
progress_callback(transferred, total_size)
|
|
530
|
-
self._mpy_comm.exec("f.close()")
|
|
533
|
+
self._mpy_comm.exec("f.close()", timeout=FLASH_IO_TIMEOUT)
|
|
531
534
|
# Run garbage collection to free memory and allow flash to settle
|
|
532
535
|
self.import_module('gc')
|
|
533
|
-
self._mpy_comm.exec("gc.collect()")
|
|
536
|
+
self._mpy_comm.exec("gc.collect()", timeout=FLASH_IO_TIMEOUT)
|
|
534
537
|
return encodings_used, wire_bytes
|
|
535
538
|
|
|
536
539
|
def platform(self):
|
|
@@ -540,15 +543,20 @@ def _mt_pfind(label):
|
|
|
540
543
|
dict with keys:
|
|
541
544
|
'platform': platform name (e.g. 'esp32')
|
|
542
545
|
'version': MicroPython version string
|
|
543
|
-
'
|
|
546
|
+
'impl_name': implementation name (e.g. 'micropython')
|
|
547
|
+
'impl_version': implementation version tuple (e.g. (1, 24, 0))
|
|
544
548
|
'machine': machine description (or None)
|
|
549
|
+
'mpy_ver': mpy bytecode major version (e.g. 6) or None
|
|
550
|
+
'mpy_sub': mpy bytecode sub-version (e.g. 3) or None
|
|
545
551
|
"""
|
|
546
552
|
self.import_module('sys')
|
|
547
553
|
self.import_module('os')
|
|
548
554
|
|
|
549
555
|
platform = self._mpy_comm.exec_eval("repr(sys.platform)")
|
|
550
556
|
version = self._mpy_comm.exec_eval("repr(sys.version)")
|
|
551
|
-
|
|
557
|
+
impl_name = self._mpy_comm.exec_eval("repr(sys.implementation.name)")
|
|
558
|
+
impl_version = self._mpy_comm.exec_eval(
|
|
559
|
+
"tuple(sys.implementation.version)")
|
|
552
560
|
|
|
553
561
|
try:
|
|
554
562
|
uname = self._mpy_comm.exec_eval("tuple(os.uname())")
|
|
@@ -556,11 +564,25 @@ def _mt_pfind(label):
|
|
|
556
564
|
except _mpy_comm.CmdError:
|
|
557
565
|
machine = None
|
|
558
566
|
|
|
567
|
+
try:
|
|
568
|
+
mpy_int = self._mpy_comm.exec_eval("sys.implementation._mpy")
|
|
569
|
+
mpy_ver = mpy_int & 0xFF
|
|
570
|
+
mpy_sub = (mpy_int >> 8) & 3
|
|
571
|
+
mpy_arch = (mpy_int >> 10) & 0x0F
|
|
572
|
+
except (_mpy_comm.CmdError, AttributeError):
|
|
573
|
+
mpy_ver = None
|
|
574
|
+
mpy_sub = None
|
|
575
|
+
mpy_arch = 0
|
|
576
|
+
|
|
559
577
|
return {
|
|
560
578
|
'platform': platform,
|
|
561
579
|
'version': version,
|
|
562
|
-
'
|
|
580
|
+
'impl_name': impl_name,
|
|
581
|
+
'impl_version': impl_version,
|
|
563
582
|
'machine': machine,
|
|
583
|
+
'mpy_ver': mpy_ver,
|
|
584
|
+
'mpy_sub': mpy_sub,
|
|
585
|
+
'mpy_arch': mpy_arch,
|
|
564
586
|
}
|
|
565
587
|
|
|
566
588
|
def memory(self):
|
|
@@ -174,7 +174,8 @@ class MpyComm():
|
|
|
174
174
|
|
|
175
175
|
Arguments:
|
|
176
176
|
command: command to execute
|
|
177
|
-
timeout: maximum waiting time for result
|
|
177
|
+
timeout: maximum waiting time for result,
|
|
178
|
+
0 = submit only (send code, don't wait for output)
|
|
178
179
|
|
|
179
180
|
Returns:
|
|
180
181
|
command STDOUT result
|
|
@@ -182,12 +183,16 @@ class MpyComm():
|
|
|
182
183
|
Raises:
|
|
183
184
|
CmdError when command return error
|
|
184
185
|
"""
|
|
186
|
+
send_timeout = 5 if timeout == 0 else timeout
|
|
185
187
|
self.enter_raw_repl()
|
|
186
188
|
if self._log:
|
|
187
189
|
self._log.info("CMD: %s", command)
|
|
188
190
|
self._conn.write(bytes(command, 'utf-8'))
|
|
189
191
|
self._conn.write(CTRL_D)
|
|
190
|
-
self._conn.read_until(b'OK',
|
|
192
|
+
self._conn.read_until(b'OK', send_timeout)
|
|
193
|
+
if timeout == 0:
|
|
194
|
+
self._repl_mode = False
|
|
195
|
+
return b''
|
|
191
196
|
result = self._conn.read_until(CTRL_D, timeout)
|
|
192
197
|
if result:
|
|
193
198
|
if self._log:
|
|
@@ -209,7 +214,8 @@ class MpyComm():
|
|
|
209
214
|
|
|
210
215
|
Arguments:
|
|
211
216
|
command: command to execute (str or bytes)
|
|
212
|
-
timeout: maximum waiting time for result
|
|
217
|
+
timeout: maximum waiting time for result,
|
|
218
|
+
0 = submit only (send code, don't wait for output)
|
|
213
219
|
|
|
214
220
|
Returns:
|
|
215
221
|
command STDOUT result
|
|
@@ -218,6 +224,7 @@ class MpyComm():
|
|
|
218
224
|
CmdError when command returns error
|
|
219
225
|
MpyError when raw-paste mode is not supported
|
|
220
226
|
"""
|
|
227
|
+
send_timeout = 5 if timeout == 0 else timeout
|
|
221
228
|
self.enter_raw_repl()
|
|
222
229
|
|
|
223
230
|
if isinstance(command, str):
|
|
@@ -229,7 +236,7 @@ class MpyComm():
|
|
|
229
236
|
self._conn.write(RAW_PASTE_ENTER)
|
|
230
237
|
|
|
231
238
|
# Read response: 'R' + status (0=not supported, 1=supported)
|
|
232
|
-
header, status = self._conn.read_bytes(2,
|
|
239
|
+
header, status = self._conn.read_bytes(2, send_timeout)
|
|
233
240
|
if header != ord('R'):
|
|
234
241
|
raise MpyError(f"Unexpected raw-paste header: {header!r}")
|
|
235
242
|
|
|
@@ -243,7 +250,7 @@ class MpyComm():
|
|
|
243
250
|
self._raw_paste_supported = True
|
|
244
251
|
|
|
245
252
|
# Read window size (16-bit little-endian)
|
|
246
|
-
window_bytes = self._conn.read_bytes(2,
|
|
253
|
+
window_bytes = self._conn.read_bytes(2, send_timeout)
|
|
247
254
|
window_size = int.from_bytes(window_bytes, 'little')
|
|
248
255
|
if self._log:
|
|
249
256
|
self._log.info("Raw-paste window size: %d", window_size)
|
|
@@ -255,7 +262,7 @@ class MpyComm():
|
|
|
255
262
|
while offset < len(command):
|
|
256
263
|
# Check for incoming flow control byte (non-blocking)
|
|
257
264
|
if remaining_window == 0 or self._conn._has_data(0):
|
|
258
|
-
flow_byte = self._conn.read_bytes(1,
|
|
265
|
+
flow_byte = self._conn.read_bytes(1, send_timeout)
|
|
259
266
|
if flow_byte == RAW_PASTE_ACK:
|
|
260
267
|
remaining_window += window_size
|
|
261
268
|
elif flow_byte == CTRL_D:
|
|
@@ -274,10 +281,14 @@ class MpyComm():
|
|
|
274
281
|
|
|
275
282
|
# Consume remaining ACKs and wait for CTRL_D echo
|
|
276
283
|
while True:
|
|
277
|
-
byte = self._conn.read_bytes(1,
|
|
284
|
+
byte = self._conn.read_bytes(1, send_timeout)
|
|
278
285
|
if byte == CTRL_D:
|
|
279
286
|
break
|
|
280
287
|
|
|
288
|
+
if timeout == 0:
|
|
289
|
+
self._repl_mode = False
|
|
290
|
+
return b''
|
|
291
|
+
|
|
281
292
|
result = self._conn.read_until(CTRL_D, timeout)
|
|
282
293
|
if result and self._log:
|
|
283
294
|
self._log.info('RES: %s', bytes(result))
|