nrflash 1.0.0__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.
nrflash/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """nrflash - Termux-native .bin flasher for ESP32/ESP8266 boards.
2
+
3
+ No root, no esptool.py subprocess, no pyserial.
4
+ """
5
+
6
+ __version__ = "1.0.0"
nrflash/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
nrflash/cdc_reset.py ADDED
@@ -0,0 +1,96 @@
1
+ """
2
+ cdc_reset.py - Put a native-USB-CDC ESP32 (S2 / S3 / C3) into the ROM
3
+ download bootloader, and reset it back to running firmware afterward.
4
+
5
+ usb_device.py's init_uart_bridge() already solves this for *external*
6
+ UART bridge chips (CP2102/CH340/FTDI) by toggling DTR/RTS, which are real
7
+ GPIO-wired reset/boot lines on those boards. Native USB CDC boards
8
+ (303A:1001 / 303A:0002) have no such bridge chip and no real DTR/RTS
9
+ hardware — Espressif's USB-CDC-ACM stack on these chips instead exposes a
10
+ *software* convention over CDC "SET_LINE_CODING" / control transfers:
11
+
12
+ - Standard CDC has no boot-strap pins, so Espressif's `esptool.py`
13
+ triggers entry into the ROM bootloader two different ways depending
14
+ on board age/firmware:
15
+
16
+ 1. "USB-JTAG/serial" reset hack: send a CDC `SET_CONTROL_LINE_STATE`
17
+ class request with specific DTR/RTS *bit patterns* — the chip's
18
+ internal USB-Serial-JTAG peripheral watches these bits in
19
+ hardware and maps them to EN/BOOT internally, the same logic
20
+ CP2102/CH340 boards do externally with real wires.
21
+
22
+ 2. If that doesn't trigger it (older bootloader ROMs, or the
23
+ sketch never enabled the watch), we fall back to asking the
24
+ *currently running sketch* to reboot into bootloader via the
25
+ well-known "0xAD 0xDE" magic isn't relevant here -- that's
26
+ NRcap32's own app protocol, not a chip-level reset path, so we
27
+ do NOT depend on the firmware cooperating. Native USB CDC entry
28
+ must work even when the flash is blank/corrupt.
29
+
30
+ This module only implements path (1), which is what every ESP32-S3/C3/S2
31
+ dev board with native USB (including bare modules wired per Espressif's
32
+ reference) supports at the silicon level — it's part of the USB-Serial-JTAG
33
+ peripheral, not the user sketch.
34
+ """
35
+
36
+ import time
37
+
38
+ # bmRequestType for CDC class-specific request, host->device
39
+ _CDC_REQTYPE = 0x21
40
+ _SET_CONTROL_LINE_STATE = 0x22
41
+
42
+ # DTR/RTS bit positions inside wValue of SET_CONTROL_LINE_STATE
43
+ _DTR = 0x01
44
+ _RTS = 0x02
45
+
46
+
47
+ def _set_line_state(device, dtr: bool, rts: bool):
48
+ value = (_DTR if dtr else 0) | (_RTS if rts else 0)
49
+ # wIndex is the CDC control interface number; native ESP32 USB CDC
50
+ # always exposes it as interface 0 (data class lives on interface 1,
51
+ # matched by get_cdc_endpoints()'s native-CDC branch).
52
+ device.ctrl_transfer(_CDC_REQTYPE, _SET_CONTROL_LINE_STATE, value, 0, None)
53
+
54
+
55
+ def enter_bootloader(device, settle: float = 0.3):
56
+ """
57
+ Drive the native USB-CDC reset-into-bootloader sequence.
58
+
59
+ Sequence (matches the espressif esptool 'usb_jtag_serial' reset
60
+ strategy, re-derived from the public USB-Serial-JTAG peripheral
61
+ behavior, no esptool code reused):
62
+
63
+ 1. DTR=1, RTS=0 -> internal EN released (chip held in reset briefly)
64
+ 2. DTR=0, RTS=1 -> internal BOOT pulled low, EN toggled -> reset
65
+ into bootloader because BOOT is sampled low
66
+ 3. DTR=0, RTS=0 -> release lines, let ROM bootloader start cleanly
67
+
68
+ Some boards need this whole sequence sent twice back-to-back because
69
+ the very first control transfer after USB enumeration can be eaten by
70
+ the host controller settling — harmless to repeat, the chip is already
71
+ held in reset for the duration.
72
+ """
73
+ for _ in range(2):
74
+ _set_line_state(device, dtr=True, rts=False)
75
+ time.sleep(0.05)
76
+ _set_line_state(device, dtr=False, rts=True)
77
+ time.sleep(0.05)
78
+ _set_line_state(device, dtr=False, rts=False)
79
+ time.sleep(0.05)
80
+ time.sleep(settle)
81
+
82
+
83
+ def reset_to_app(device, settle: float = 0.5):
84
+ """
85
+ Reset the chip back out of the bootloader into the normal app
86
+ (whatever was just flashed, or the existing firmware if the user
87
+ only wanted to probe/verify).
88
+
89
+ 1. DTR=1, RTS=1 -> EN pulled low -> reset asserted
90
+ 2. DTR=0, RTS=0 -> EN released, BOOT high (not sampled)
91
+ -> boots the flashed application, not the ROM loader
92
+ """
93
+ _set_line_state(device, dtr=True, rts=True)
94
+ time.sleep(0.1)
95
+ _set_line_state(device, dtr=False, rts=False)
96
+ time.sleep(settle)