mpytool 1.2.0__tar.gz → 2.0.0__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.
Files changed (34) hide show
  1. mpytool-2.0.0/PKG-INFO +233 -0
  2. mpytool-2.0.0/README.md +219 -0
  3. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool/__init__.py +2 -0
  4. mpytool-2.0.0/mpytool/conn.py +100 -0
  5. mpytool-2.0.0/mpytool/conn_serial.py +34 -0
  6. mpytool-2.0.0/mpytool/conn_socket.py +53 -0
  7. mpytool-2.0.0/mpytool/logger.py +87 -0
  8. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool/mpy.py +79 -14
  9. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool/mpy_comm.py +44 -15
  10. mpytool-2.0.0/mpytool/mpytool.py +913 -0
  11. mpytool-2.0.0/mpytool/terminal.py +76 -0
  12. mpytool-2.0.0/mpytool/utils.py +82 -0
  13. mpytool-2.0.0/mpytool.egg-info/PKG-INFO +233 -0
  14. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool.egg-info/SOURCES.txt +9 -3
  15. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool.egg-info/entry_points.txt +0 -1
  16. mpytool-2.0.0/pyproject.toml +24 -0
  17. mpytool-2.0.0/tests/test_errors.py +64 -0
  18. mpytool-2.0.0/tests/test_integration.py +551 -0
  19. mpytool-2.0.0/tests/test_mpy.py +27 -0
  20. mpytool-2.0.0/tests/test_utils.py +145 -0
  21. mpytool-1.2.0/PKG-INFO +0 -22
  22. mpytool-1.2.0/README.md +0 -124
  23. mpytool-1.2.0/mpytool/__about__.py +0 -12
  24. mpytool-1.2.0/mpytool/conn.py +0 -38
  25. mpytool-1.2.0/mpytool/conn_serial.py +0 -67
  26. mpytool-1.2.0/mpytool/mpytool.py +0 -328
  27. mpytool-1.2.0/mpytool/terminal.py +0 -47
  28. mpytool-1.2.0/mpytool.egg-info/PKG-INFO +0 -22
  29. mpytool-1.2.0/setup.py +0 -47
  30. {mpytool-1.2.0 → mpytool-2.0.0}/LICENSE +0 -0
  31. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool.egg-info/dependency_links.txt +0 -0
  32. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool.egg-info/requires.txt +0 -0
  33. {mpytool-1.2.0 → mpytool-2.0.0}/mpytool.egg-info/top_level.txt +0 -0
  34. {mpytool-1.2.0 → mpytool-2.0.0}/setup.cfg +0 -0
mpytool-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: mpytool
3
+ Version: 2.0.0
4
+ Summary: MPY tool - manage files on devices running MicroPython
5
+ Author-email: Pavel Revak <pavel.revak@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pavelrevak/mpytool
8
+ Keywords: MPY,micropython
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pyserial>=3.0
13
+ Dynamic: license-file
14
+
15
+ # mpytool
16
+
17
+ MPY tool - manage files on devices running MicroPython
18
+
19
+ It is an alternative to [ampy](https://github.com/scientifichackers/ampy)
20
+
21
+ Target of this project is to make more clean code, faster, better verbose output...
22
+
23
+ ## Installation
24
+
25
+ ```
26
+ pip3 install mpytool
27
+ ```
28
+
29
+ ## Examples:
30
+
31
+ help:
32
+ ```
33
+ $ mpytool --help
34
+ ```
35
+
36
+ list files:
37
+ ```
38
+ $ mpytool -p /dev/ttyACM0 ls
39
+ $ mpytool -p /dev/ttyACM0 ls lib
40
+ ```
41
+
42
+ tree files:
43
+ ```
44
+ $ mpytool -p /dev/ttyACM0 tree
45
+ ```
46
+
47
+ copy files (: prefix = device path):
48
+ ```
49
+ $ mpytool cp main.py :/ # upload file to device root
50
+ $ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
51
+ $ mpytool cp myapp/ :/ # upload directory (creates /myapp/)
52
+ $ mpytool cp myapp/ :/lib/ # upload directory into /lib/
53
+ $ mpytool cp :/main.py ./ # download file to current directory
54
+ $ mpytool cp :/ ./backup/ # download entire device to backup/
55
+ $ mpytool cp :/old.py :/new.py # copy file on device
56
+ $ mpytool cp -f main.py :/ # force upload even if unchanged
57
+ ```
58
+
59
+ Unchanged files are automatically skipped (compares size and SHA256 hash).
60
+ Use `-f` or `--force` to upload all files regardless.
61
+
62
+ move/rename on device:
63
+ ```
64
+ $ mpytool mv :/old.py :/new.py # rename file
65
+ $ mpytool mv :/file.py :/lib/ # move file to directory
66
+ $ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
67
+ ```
68
+
69
+ legacy upload/download (still available):
70
+ ```
71
+ $ mpytool put boot.py /
72
+ $ mpytool get boot.py >> boot.py
73
+ ```
74
+
75
+ make directory, delete files:
76
+ ```
77
+ $ mpytool mkdir a/b/c/d xyz/abc # create directories
78
+ $ mpytool rm mydir # delete directory and contents
79
+ $ mpytool rm mydir/ # delete contents only, keep directory
80
+ $ mpytool rm / # delete everything on device
81
+ ```
82
+
83
+ reset only, reset and follow output, REPL mode:
84
+ ```
85
+ $ mpytool -p /dev/ttyACM0 reset
86
+ $ mpytool -p /dev/ttyACM0 reset follow
87
+ $ mpytool -p /dev/ttyACM0 repl
88
+ ```
89
+
90
+ execute Python code on device:
91
+ ```
92
+ $ mpytool exec "print('Hello!')"
93
+ $ mpytool exec "import sys; print(sys.version)"
94
+ ```
95
+
96
+ show device information:
97
+ ```
98
+ $ mpytool info
99
+ Platform: rp2
100
+ Version: 3.4.0; MicroPython v1.27.0 on 2025-12-09
101
+ Impl: micropython
102
+ Machine: Raspberry Pi Pico with RP2040
103
+ Memory: 36.4 KB / 240 KB (15.15%)
104
+ Flash: 120 KB / 1.38 MB (8.52%)
105
+ ```
106
+
107
+ multiple commands separated by `--`:
108
+ ```
109
+ $ mpytool -p /dev/ttyACM0 put main.py / -- reset -- follow
110
+ $ mpytool -p /dev/ttyACM0 delete old.py -- put new.py / -- reset
111
+ ```
112
+
113
+ auto-detect serial port (if only one device is connected):
114
+ ```
115
+ $ mpytool ls
116
+ Using /dev/ttyACM0
117
+ 215 boot.py
118
+ 2938 net.py
119
+ ```
120
+
121
+ connect over network (TCP, default port 23):
122
+ ```
123
+ $ mpytool -a 192.168.1.100 ls
124
+ $ mpytool -a 192.168.1.100:8266 tree
125
+ ```
126
+
127
+ set baudrate (default 115200):
128
+ ```
129
+ $ mpytool -p /dev/ttyACM0 -b 9600 ls
130
+ ```
131
+
132
+ show version:
133
+ ```
134
+ $ mpytool -V
135
+ ```
136
+
137
+ command aliases:
138
+ - `dir` = `ls`
139
+ - `cat` = `get`
140
+ - `del`, `rm` = `delete`
141
+
142
+ ## Examples using API from Python
143
+
144
+ ```
145
+ >>> import mpytool
146
+ >>> conn = mpytool.ConnSerial(port='/dev/ttyACM0', baudrate=115200)
147
+ >>> mpy = mpytool.Mpy(conn)
148
+ >>> mpy.ls()
149
+ [('ehome', None), ('boot.py', 215), ('net.py', 2938), ('project.json', 6404)]
150
+ >>> mpy.mkdir('a/b/c')
151
+ >>> mpy.ls()
152
+ [('a', None),
153
+ ('ehome', None),
154
+ ('boot.py', 215),
155
+ ('net.py', 2938),
156
+ ('project.json', 6404)]
157
+ >>> mpy.get('boot.py')
158
+ b"import time\nimport net\n\nwlan = net.Wlan()\nwlan.refresh_network()\n\nwhile wlan.ifconfig()[0] == '0.0.0.0':\n time.sleep(.1)\n\nprint('IP: ' + wlan.ifconfig()[0])\n\nimport ehome.ehome\n\nehome.ehome.start('project.json')\n"
159
+ >>> mpy.delete('a/b')
160
+ ```
161
+
162
+ ## Progress and verbose output
163
+
164
+ Progress is shown by default during file transfers:
165
+ ```
166
+ $ mpytool cp main.py lib.py :/lib/
167
+ [1/2] 100% 1.2KB main.py -> :/lib/main.py
168
+ [2/2] 100% 3.4KB lib.py -> :/lib/lib.py
169
+ ```
170
+
171
+ use `-v` or `--verbose` to also show commands being executed:
172
+ ```
173
+ $ mpytool -v rm /old.py
174
+ delete: /old.py
175
+ ```
176
+
177
+ use `-q` or `--quiet` to disable all output:
178
+ ```
179
+ $ mpytool -q cp main.py :/
180
+ ```
181
+
182
+ ## Debug output
183
+
184
+ - `-d` print warnings (yellow)
185
+ - `-dd` print info messages (purple)
186
+ - `-ddd` print debug messages (blue)
187
+
188
+ for reporting bugs, please provide in to issue also -ddd messages
189
+
190
+ ## MPYTOOL vs other tools
191
+
192
+ Benchmark on RP2040 (Raspberry Pi Pico) over native USB, January 2025:
193
+
194
+ | Test | mpytool | mpremote |
195
+ |------|---------|----------|
196
+ | 50 small files, 5 dirs (200KB) | **4.2s** | 9.4s |
197
+ | 5 large files (260KB) | **8.0s** | 17.3s |
198
+ | re-upload unchanged files | **1.3s** | 5.0s |
199
+
200
+ mpytool is **2x faster** for uploads and **4x faster** for re-uploads (skips unchanged files).
201
+
202
+ mpytool advantages:
203
+ - Fastest file transfers (2x faster than mpremote)
204
+ - Skip unchanged files (compares size + SHA256 hash)
205
+ - Progress indicator with file counts (`[3/10] 50% file.py -> :/lib/`)
206
+ - Single tool for all operations (no need to chain commands)
207
+ - Clean verbose output (`-v`) for debugging
208
+
209
+ ## Requirements
210
+
211
+ Working only with MicroPython boards, not with CircuitPython
212
+
213
+ - python v3.10+
214
+ - pyserial v3.0+
215
+
216
+ ### Running on:
217
+
218
+ - Linux
219
+ - MacOS
220
+ - Windows (limited support - REPL mode is disabled)
221
+
222
+ ## Credits
223
+
224
+ (c) 2022 by Pavel Revak
225
+
226
+ ### License
227
+
228
+ MIT
229
+
230
+ ### Support
231
+
232
+ - Basic support is free over GitHub issues.
233
+ - Professional support is available over email: [Pavel Revak](mailto:pavel.revak@gmail.com?subject=[GitHub]%20mpytool).
@@ -0,0 +1,219 @@
1
+ # mpytool
2
+
3
+ MPY tool - manage files on devices running MicroPython
4
+
5
+ It is an alternative to [ampy](https://github.com/scientifichackers/ampy)
6
+
7
+ Target of this project is to make more clean code, faster, better verbose output...
8
+
9
+ ## Installation
10
+
11
+ ```
12
+ pip3 install mpytool
13
+ ```
14
+
15
+ ## Examples:
16
+
17
+ help:
18
+ ```
19
+ $ mpytool --help
20
+ ```
21
+
22
+ list files:
23
+ ```
24
+ $ mpytool -p /dev/ttyACM0 ls
25
+ $ mpytool -p /dev/ttyACM0 ls lib
26
+ ```
27
+
28
+ tree files:
29
+ ```
30
+ $ mpytool -p /dev/ttyACM0 tree
31
+ ```
32
+
33
+ copy files (: prefix = device path):
34
+ ```
35
+ $ mpytool cp main.py :/ # upload file to device root
36
+ $ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
37
+ $ mpytool cp myapp/ :/ # upload directory (creates /myapp/)
38
+ $ mpytool cp myapp/ :/lib/ # upload directory into /lib/
39
+ $ mpytool cp :/main.py ./ # download file to current directory
40
+ $ mpytool cp :/ ./backup/ # download entire device to backup/
41
+ $ mpytool cp :/old.py :/new.py # copy file on device
42
+ $ mpytool cp -f main.py :/ # force upload even if unchanged
43
+ ```
44
+
45
+ Unchanged files are automatically skipped (compares size and SHA256 hash).
46
+ Use `-f` or `--force` to upload all files regardless.
47
+
48
+ move/rename on device:
49
+ ```
50
+ $ mpytool mv :/old.py :/new.py # rename file
51
+ $ mpytool mv :/file.py :/lib/ # move file to directory
52
+ $ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
53
+ ```
54
+
55
+ legacy upload/download (still available):
56
+ ```
57
+ $ mpytool put boot.py /
58
+ $ mpytool get boot.py >> boot.py
59
+ ```
60
+
61
+ make directory, delete files:
62
+ ```
63
+ $ mpytool mkdir a/b/c/d xyz/abc # create directories
64
+ $ mpytool rm mydir # delete directory and contents
65
+ $ mpytool rm mydir/ # delete contents only, keep directory
66
+ $ mpytool rm / # delete everything on device
67
+ ```
68
+
69
+ reset only, reset and follow output, REPL mode:
70
+ ```
71
+ $ mpytool -p /dev/ttyACM0 reset
72
+ $ mpytool -p /dev/ttyACM0 reset follow
73
+ $ mpytool -p /dev/ttyACM0 repl
74
+ ```
75
+
76
+ execute Python code on device:
77
+ ```
78
+ $ mpytool exec "print('Hello!')"
79
+ $ mpytool exec "import sys; print(sys.version)"
80
+ ```
81
+
82
+ show device information:
83
+ ```
84
+ $ mpytool info
85
+ Platform: rp2
86
+ Version: 3.4.0; MicroPython v1.27.0 on 2025-12-09
87
+ Impl: micropython
88
+ Machine: Raspberry Pi Pico with RP2040
89
+ Memory: 36.4 KB / 240 KB (15.15%)
90
+ Flash: 120 KB / 1.38 MB (8.52%)
91
+ ```
92
+
93
+ multiple commands separated by `--`:
94
+ ```
95
+ $ mpytool -p /dev/ttyACM0 put main.py / -- reset -- follow
96
+ $ mpytool -p /dev/ttyACM0 delete old.py -- put new.py / -- reset
97
+ ```
98
+
99
+ auto-detect serial port (if only one device is connected):
100
+ ```
101
+ $ mpytool ls
102
+ Using /dev/ttyACM0
103
+ 215 boot.py
104
+ 2938 net.py
105
+ ```
106
+
107
+ connect over network (TCP, default port 23):
108
+ ```
109
+ $ mpytool -a 192.168.1.100 ls
110
+ $ mpytool -a 192.168.1.100:8266 tree
111
+ ```
112
+
113
+ set baudrate (default 115200):
114
+ ```
115
+ $ mpytool -p /dev/ttyACM0 -b 9600 ls
116
+ ```
117
+
118
+ show version:
119
+ ```
120
+ $ mpytool -V
121
+ ```
122
+
123
+ command aliases:
124
+ - `dir` = `ls`
125
+ - `cat` = `get`
126
+ - `del`, `rm` = `delete`
127
+
128
+ ## Examples using API from Python
129
+
130
+ ```
131
+ >>> import mpytool
132
+ >>> conn = mpytool.ConnSerial(port='/dev/ttyACM0', baudrate=115200)
133
+ >>> mpy = mpytool.Mpy(conn)
134
+ >>> mpy.ls()
135
+ [('ehome', None), ('boot.py', 215), ('net.py', 2938), ('project.json', 6404)]
136
+ >>> mpy.mkdir('a/b/c')
137
+ >>> mpy.ls()
138
+ [('a', None),
139
+ ('ehome', None),
140
+ ('boot.py', 215),
141
+ ('net.py', 2938),
142
+ ('project.json', 6404)]
143
+ >>> mpy.get('boot.py')
144
+ b"import time\nimport net\n\nwlan = net.Wlan()\nwlan.refresh_network()\n\nwhile wlan.ifconfig()[0] == '0.0.0.0':\n time.sleep(.1)\n\nprint('IP: ' + wlan.ifconfig()[0])\n\nimport ehome.ehome\n\nehome.ehome.start('project.json')\n"
145
+ >>> mpy.delete('a/b')
146
+ ```
147
+
148
+ ## Progress and verbose output
149
+
150
+ Progress is shown by default during file transfers:
151
+ ```
152
+ $ mpytool cp main.py lib.py :/lib/
153
+ [1/2] 100% 1.2KB main.py -> :/lib/main.py
154
+ [2/2] 100% 3.4KB lib.py -> :/lib/lib.py
155
+ ```
156
+
157
+ use `-v` or `--verbose` to also show commands being executed:
158
+ ```
159
+ $ mpytool -v rm /old.py
160
+ delete: /old.py
161
+ ```
162
+
163
+ use `-q` or `--quiet` to disable all output:
164
+ ```
165
+ $ mpytool -q cp main.py :/
166
+ ```
167
+
168
+ ## Debug output
169
+
170
+ - `-d` print warnings (yellow)
171
+ - `-dd` print info messages (purple)
172
+ - `-ddd` print debug messages (blue)
173
+
174
+ for reporting bugs, please provide in to issue also -ddd messages
175
+
176
+ ## MPYTOOL vs other tools
177
+
178
+ Benchmark on RP2040 (Raspberry Pi Pico) over native USB, January 2025:
179
+
180
+ | Test | mpytool | mpremote |
181
+ |------|---------|----------|
182
+ | 50 small files, 5 dirs (200KB) | **4.2s** | 9.4s |
183
+ | 5 large files (260KB) | **8.0s** | 17.3s |
184
+ | re-upload unchanged files | **1.3s** | 5.0s |
185
+
186
+ mpytool is **2x faster** for uploads and **4x faster** for re-uploads (skips unchanged files).
187
+
188
+ mpytool advantages:
189
+ - Fastest file transfers (2x faster than mpremote)
190
+ - Skip unchanged files (compares size + SHA256 hash)
191
+ - Progress indicator with file counts (`[3/10] 50% file.py -> :/lib/`)
192
+ - Single tool for all operations (no need to chain commands)
193
+ - Clean verbose output (`-v`) for debugging
194
+
195
+ ## Requirements
196
+
197
+ Working only with MicroPython boards, not with CircuitPython
198
+
199
+ - python v3.10+
200
+ - pyserial v3.0+
201
+
202
+ ### Running on:
203
+
204
+ - Linux
205
+ - MacOS
206
+ - Windows (limited support - REPL mode is disabled)
207
+
208
+ ## Credits
209
+
210
+ (c) 2022 by Pavel Revak
211
+
212
+ ### License
213
+
214
+ MIT
215
+
216
+ ### Support
217
+
218
+ - Basic support is free over GitHub issues.
219
+ - Professional support is available over email: [Pavel Revak](mailto:pavel.revak@gmail.com?subject=[GitHub]%20mpytool).
@@ -2,5 +2,7 @@
2
2
 
3
3
  from mpytool.conn import ConnError, Timeout
4
4
  from mpytool.conn_serial import ConnSerial
5
+ from mpytool.conn_socket import ConnSocket
5
6
  from mpytool.mpy_comm import MpyError, CmdError
6
7
  from mpytool.mpy import Mpy
8
+ from mpytool.logger import SimpleColorLogger
@@ -0,0 +1,100 @@
1
+ """MicroPython tool: abstract connector"""
2
+
3
+ import time as _time
4
+ import select as _select
5
+
6
+
7
+ class ConnError(Exception):
8
+ """General connection error"""
9
+
10
+
11
+ class Timeout(ConnError):
12
+ """Timeout"""
13
+
14
+
15
+ class Conn():
16
+ def __init__(self, log=None):
17
+ self._log = log
18
+ self._buffer = bytearray(b'')
19
+
20
+ @property
21
+ def fd(self):
22
+ """Return file descriptor for select()"""
23
+ return None
24
+
25
+ def _has_data(self, timeout=0):
26
+ """Check if data is available to read using select()"""
27
+ fd = self.fd
28
+ if fd is None:
29
+ return False
30
+ readable, _, _ = _select.select([fd], [], [], timeout)
31
+ return bool(readable)
32
+
33
+ def _read_available(self):
34
+ """Read available data from device (must be implemented by subclass)"""
35
+ raise NotImplementedError
36
+
37
+ def _write_raw(self, data):
38
+ """Write data to device, return bytes written (must be implemented by subclass)"""
39
+ raise NotImplementedError
40
+
41
+ def _read_to_buffer(self, wait_timeout=0):
42
+ """Read available data into buffer
43
+
44
+ Arguments:
45
+ wait_timeout: how long to wait for data (0 = non-blocking)
46
+ """
47
+ if self._has_data(wait_timeout):
48
+ data = self._read_available()
49
+ if data:
50
+ self._buffer += data
51
+ return True
52
+ return False
53
+
54
+ def flush(self):
55
+ """Flush and return buffer contents"""
56
+ buffer = bytes(self._buffer)
57
+ del self._buffer[:]
58
+ return buffer
59
+
60
+ def read(self):
61
+ """Read available data from device"""
62
+ if self._has_data():
63
+ return self._read_available()
64
+ return None
65
+
66
+ def write(self, data):
67
+ """Write data to device"""
68
+ if self._log:
69
+ self._log.debug("wr: %s", bytes(data))
70
+ while data:
71
+ count = self._write_raw(data)
72
+ data = data[count:]
73
+
74
+ def read_until(self, end, timeout=1):
75
+ """Read until end marker is found"""
76
+ if self._log:
77
+ self._log.debug("wait for %s", end)
78
+ start_time = _time.time()
79
+ while True:
80
+ # Use select() with 1ms timeout instead of sleep - wakes immediately on data
81
+ if self._read_to_buffer(wait_timeout=0.001):
82
+ start_time = _time.time() # reset timeout on data received
83
+ if end in self._buffer:
84
+ break
85
+ if timeout is not None and start_time + timeout < _time.time():
86
+ if self._buffer:
87
+ raise Timeout(
88
+ f"During timeout received: {bytes(self._buffer)}")
89
+ raise Timeout("No data received")
90
+ index = self._buffer.index(end)
91
+ data = self._buffer[:index]
92
+ del self._buffer[:index + len(end)]
93
+ if self._log:
94
+ self._log.debug("rd: %s", bytes(data + end))
95
+ return data
96
+
97
+ def read_line(self, timeout=None):
98
+ """Read single line"""
99
+ line = self.read_until(b'\n', timeout)
100
+ return line.strip(b'\r')
@@ -0,0 +1,34 @@
1
+ """MicroPython tool: serial connector"""
2
+
3
+ import serial as _serial
4
+ import mpytool.conn as _conn
5
+
6
+
7
+ class ConnSerial(_conn.Conn):
8
+ def __init__(self, log=None, **serial_config):
9
+ super().__init__(log)
10
+ try:
11
+ self._serial = _serial.Serial(**serial_config)
12
+ except _serial.serialutil.SerialException as err:
13
+ self._serial = None
14
+ raise _conn.ConnError(
15
+ f"Error opening serial port {serial_config['port']}") from err
16
+
17
+ def __del__(self):
18
+ if self._serial:
19
+ self._serial.close()
20
+
21
+ @property
22
+ def fd(self):
23
+ return self._serial.fd if self._serial else None
24
+
25
+ def _read_available(self):
26
+ """Read available data from serial port"""
27
+ in_waiting = self._serial.in_waiting
28
+ if in_waiting > 0:
29
+ return self._serial.read(in_waiting)
30
+ return None
31
+
32
+ def _write_raw(self, data):
33
+ """Write data to serial port"""
34
+ return self._serial.write(data)
@@ -0,0 +1,53 @@
1
+ """MicroPython tool: socket connector"""
2
+
3
+ import socket as _socket
4
+ import mpytool.conn as _conn
5
+
6
+
7
+ class ConnSocket(_conn.Conn):
8
+ def __init__(self, address, log=None):
9
+ super().__init__(log)
10
+ self._socket = None
11
+ sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
12
+ sock.settimeout(5)
13
+ if ':' in address:
14
+ host, port = address.split(':')
15
+ port = int(port)
16
+ else:
17
+ host = address
18
+ port = 23
19
+ if log:
20
+ log.info(f"Connecting to: {host}:{port}")
21
+ try:
22
+ sock.connect((host, port))
23
+ except _socket.timeout as err:
24
+ raise _conn.ConnError(f"Timeout connecting to {host}:{port}") from err
25
+ except _socket.error as err:
26
+ raise _conn.ConnError(f"Cannot connect to {host}:{port}: {err}") from err
27
+ sock.settimeout(None)
28
+ sock.setblocking(False)
29
+ self._socket = sock
30
+ if log:
31
+ log.info("connected")
32
+
33
+ def __del__(self):
34
+ if self._socket:
35
+ self._socket.close()
36
+
37
+ @property
38
+ def fd(self):
39
+ return self._socket.fileno() if self._socket else None
40
+
41
+ def _read_available(self):
42
+ """Read available data from socket"""
43
+ try:
44
+ data = self._socket.recv(4096)
45
+ if data:
46
+ return data
47
+ except BlockingIOError:
48
+ pass
49
+ return None
50
+
51
+ def _write_raw(self, data):
52
+ """Write data to socket"""
53
+ return self._socket.send(data)