espzero 0.0.1__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.
- espzero-0.0.1.dist-info/METADATA +409 -0
- espzero-0.0.1.dist-info/RECORD +8 -0
- espzero-0.0.1.dist-info/WHEEL +5 -0
- espzero-0.0.1.dist-info/top_level.txt +1 -0
- profiles/__init__.py +5 -0
- profiles/_base.py +83 -0
- profiles/auto.py +50 -0
- profiles/esp32_boards.py +254 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: espzero
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Educational tool for physical computing and ESP32/ESP8266 development.
|
|
5
|
+
Author-email: Neo Neopia-uz <neopia.uz@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/roboticsware/espzero
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/roboticsware/espzero/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# espzero
|
|
15
|
+
|
|
16
|
+
> A single MicroPython library that ports [picozero](https://github.com/raspberrypilearning/picozero) to the ESP32 family (WROOM, S3, C3, and more).
|
|
17
|
+
> **"One codebase, many boards"** — hardware differences are absorbed by the Board Profile system.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Copy the `espzero/` directory to the root of your ESP32 filesystem using Mu Editor, Thonny, or `mpremote`:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
mpremote cp -r espzero/ :espzero/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import espzero
|
|
35
|
+
espzero.begin() # initialise: auto-detect board
|
|
36
|
+
|
|
37
|
+
# Built-in LED — available after begin()
|
|
38
|
+
from espzero import esp_led
|
|
39
|
+
from time import sleep
|
|
40
|
+
|
|
41
|
+
while True:
|
|
42
|
+
esp_led.on()
|
|
43
|
+
sleep(1)
|
|
44
|
+
esp_led.off()
|
|
45
|
+
sleep(1)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Specify a board explicitly instead of auto-detection:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import espzero
|
|
52
|
+
espzero.begin("esp32_38pin_nodemcu") # or "esp32_devkit_v1", "esp8266_lolin_v3", ...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use other components after `begin()`:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from espzero import LED, Button, Servo, WiFi
|
|
59
|
+
|
|
60
|
+
led = LED("internal") # built-in LED — profile maps alias to real GPIO
|
|
61
|
+
btn = Button(0) # GPIO 0 (BOOT button)
|
|
62
|
+
led.blink(on_time=0.5, n=5) # identical API to picozero
|
|
63
|
+
|
|
64
|
+
# WiFi (ESP32-specific)
|
|
65
|
+
wifi = WiFi()
|
|
66
|
+
ip = wifi.connect("MySSID", "password")
|
|
67
|
+
print("IP:", ip)
|
|
68
|
+
|
|
69
|
+
# Capacitive touch (WROOM/WROVER only)
|
|
70
|
+
from espzero import CapTouch
|
|
71
|
+
touch = CapTouch(pin=4) # GPIO 4 = T0
|
|
72
|
+
if touch.is_touched:
|
|
73
|
+
esp_led.on()
|
|
74
|
+
|
|
75
|
+
# Servo
|
|
76
|
+
servo = Servo(13)
|
|
77
|
+
servo.mid()
|
|
78
|
+
servo.value = 0.75 # 0–1 range, same as picozero
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 1. File Structure
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
espzero/
|
|
87
|
+
├── __init__.py # Public API entry point + begin()
|
|
88
|
+
├── _hal.py # Hardware Abstraction Layer (HAL)
|
|
89
|
+
├── _core.py # Ported picozero core logic
|
|
90
|
+
├── _wifi.py # ESP32-specific: WiFi class
|
|
91
|
+
├── _touch.py # ESP32-specific: capacitive touch class
|
|
92
|
+
└── profiles/
|
|
93
|
+
├── _base.py # Abstract BoardProfile base class
|
|
94
|
+
├── auto.py # Runtime board auto-detection
|
|
95
|
+
└── esp32_boards.py # Profile definitions for all supported boards
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 2. Board Profile System
|
|
101
|
+
|
|
102
|
+
### 2-A. Base Class (`profiles/_base.py`)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
class BoardProfile:
|
|
106
|
+
NAME = "unknown" # Human-readable board name
|
|
107
|
+
CHIP = "esp32" # esp32 / esp32s3 / esp32c3 / esp8266
|
|
108
|
+
|
|
109
|
+
# Pin aliases — lets users write LED("internal") instead of LED(2)
|
|
110
|
+
PIN_ALIASES = {}
|
|
111
|
+
|
|
112
|
+
# ADC settings
|
|
113
|
+
ADC_MAX_RAW = 4095 # 12-bit resolution (0–4095)
|
|
114
|
+
ADC_SCALE = 65535 # Scale target for picozero read_u16() compatibility
|
|
115
|
+
ADC_ATTEN = None # e.g. machine.ADC.ATTN_11DB; None = firmware default
|
|
116
|
+
ADC_VREF = 3.3 # Maximum input voltage (tied to attenuation)
|
|
117
|
+
|
|
118
|
+
# PWM settings
|
|
119
|
+
PWM_DEFAULT_FREQ = 1000 # Hz — Pico default was 100 Hz; 1 kHz recommended for ESP32
|
|
120
|
+
PWM_DUTY_MAX = 65535 # Internal scale is always 16-bit
|
|
121
|
+
|
|
122
|
+
# Servo
|
|
123
|
+
SERVO_FREQ = 50 # 50 Hz (20 ms frame) — same as picozero
|
|
124
|
+
|
|
125
|
+
# Built-in LED type
|
|
126
|
+
# "digital" — standard GPIO LED
|
|
127
|
+
# "neopixel" — WS2812 RGB (e.g. ESP32-S3 DevKit, M5Stack ATOM)
|
|
128
|
+
INTERNAL_LED_TYPE = "digital"
|
|
129
|
+
INTERNAL_LED_ACTIVE_HIGH = True
|
|
130
|
+
|
|
131
|
+
# Boot strapping pins — connecting a button here may cause boot failures
|
|
132
|
+
STRAPPING_PINS = [] # e.g. [0, 2, 5, 12, 15]
|
|
133
|
+
|
|
134
|
+
# ADC2 pins — cannot be used while WiFi is active
|
|
135
|
+
ADC2_PINS = [] # e.g. [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 2-B. Board Profile Examples (`profiles/esp32_boards.py`)
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
class ESP32DevKitV1(BoardProfile):
|
|
142
|
+
NAME = "esp32_devkit_v1"
|
|
143
|
+
CHIP = "esp32"
|
|
144
|
+
PIN_ALIASES = {"internal": 2, "led": 2}
|
|
145
|
+
ADC_ATTEN = ADC.ATTN_11DB # 0–3.6 V
|
|
146
|
+
ADC_VREF = 3.6
|
|
147
|
+
INTERNAL_LED_TYPE = "digital"
|
|
148
|
+
INTERNAL_LED_ACTIVE_HIGH = False # active-low
|
|
149
|
+
STRAPPING_PINS = [0, 2, 5, 12, 15]
|
|
150
|
+
ADC2_PINS = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ESP32S3DevKit(BoardProfile):
|
|
154
|
+
NAME = "esp32_s3_devkit"
|
|
155
|
+
CHIP = "esp32s3"
|
|
156
|
+
PIN_ALIASES = {"internal": 48, "led": 48}
|
|
157
|
+
INTERNAL_LED_TYPE = "neopixel" # WS2812 RGB on GPIO 48
|
|
158
|
+
STRAPPING_PINS = [0, 3, 45, 46]
|
|
159
|
+
ADC2_PINS = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ESP32C3Mini(BoardProfile):
|
|
163
|
+
NAME = "esp32_c3_mini"
|
|
164
|
+
CHIP = "esp32c3"
|
|
165
|
+
PIN_ALIASES = {"internal": 8, "led": 8}
|
|
166
|
+
INTERNAL_LED_ACTIVE_HIGH = False
|
|
167
|
+
STRAPPING_PINS = [2, 8, 9]
|
|
168
|
+
ADC2_PINS = [] # C3 has no ADC2
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2-C. Auto-detection (`profiles/auto.py`)
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
import sys
|
|
175
|
+
|
|
176
|
+
def detect() -> str:
|
|
177
|
+
"""
|
|
178
|
+
Identify the chip from sys.implementation._machine.
|
|
179
|
+
Returns a board key that matches an entry in espzero._PROFILE_MAP.
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
machine_str = sys.implementation._machine.lower()
|
|
183
|
+
except AttributeError:
|
|
184
|
+
return "esp32_devkit_v1" # fallback
|
|
185
|
+
|
|
186
|
+
if "esp32s3" in machine_str or "esp32-s3" in machine_str:
|
|
187
|
+
return "esp32_s3_devkit"
|
|
188
|
+
elif "esp32c3" in machine_str or "esp32-c3" in machine_str:
|
|
189
|
+
return "esp32_c3_mini"
|
|
190
|
+
else:
|
|
191
|
+
return "esp32_devkit_v1" # default: WROOM
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 3. Hardware Abstraction Layer (`_hal.py`)
|
|
197
|
+
|
|
198
|
+
All `machine.Pin / machine.PWM / machine.ADC` calls are routed through this layer.
|
|
199
|
+
Swapping the board profile is enough to support a new board — `_core.py` requires no changes.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
_profile = None # Set by begin() to a BoardProfile instance
|
|
203
|
+
_wifi_active = False # True while WiFi is connected
|
|
204
|
+
|
|
205
|
+
def make_digital_in(pin, pull_up=False):
|
|
206
|
+
"""Create input Pin. Warns if GPIO is a strapping pin."""
|
|
207
|
+
gpio = resolve_pin(pin)
|
|
208
|
+
if gpio in get_profile().STRAPPING_PINS:
|
|
209
|
+
print("[espzero] WARNING: GPIO {} is a strapping pin. "
|
|
210
|
+
"Attaching a button may cause boot issues.".format(gpio))
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
def set_duty_u16(pwm_obj, value):
|
|
214
|
+
"""Set duty cycle. Falls back to duty(0–1023) on firmware < 1.19."""
|
|
215
|
+
try:
|
|
216
|
+
pwm_obj.duty_u16(value)
|
|
217
|
+
except AttributeError:
|
|
218
|
+
pwm_obj.duty(value >> 6) # 16-bit → 10-bit
|
|
219
|
+
|
|
220
|
+
def make_adc(pin):
|
|
221
|
+
"""Create ADC. Warns if WiFi is active and pin is in ADC2 group."""
|
|
222
|
+
if _wifi_active and gpio in get_profile().ADC2_PINS:
|
|
223
|
+
print("[espzero] WARNING: WiFi is active. ADC2 (GPIO {}) "
|
|
224
|
+
"cannot be used. Switch to an ADC1 pin.".format(gpio))
|
|
225
|
+
...
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 4. picozero → espzero Change Table
|
|
231
|
+
|
|
232
|
+
| Topic | picozero (Pico) | espzero (ESP32) | How |
|
|
233
|
+
|-------|----------------|-----------------|-----|
|
|
234
|
+
| **Pin creation** | `Pin(num, ...)` directly | `_hal.make_digital_out(pin)` | HAL wrapper |
|
|
235
|
+
| **PWM channel conflict** | `PIN_TO_PWM_CHANNEL[]` table | **Removed** (ESP32 LEDC: any pin, any channel) | Deleted |
|
|
236
|
+
| **PWM duty write** | `duty_u16(val)` | Same, with fallback for firmware < 1.19 | `_hal.set_duty_u16()` |
|
|
237
|
+
| **ADC read** | `adc.read_u16()` | `adc_read_u16()` → scales `read()×16` internally | HAL |
|
|
238
|
+
| **ADC attenuation** | None | `ATTN_11DB` (set in profile) | Profile |
|
|
239
|
+
| **Built-in LED** | `LED("LED")` or `LED(25)` | `LED("internal")` → profile maps to real pin | Alias system |
|
|
240
|
+
| **Built-in temp** | `pico_temp_sensor` (ADC ch.4) | `esp_temp_sensor` (ESP32 `esp32` module) | Separate class |
|
|
241
|
+
| **PWM default freq** | 100 Hz | 1000 Hz (`PWM_DEFAULT_FREQ` in profile) | Profile value |
|
|
242
|
+
| **Servo freq** | 50 Hz | 50 Hz (unchanged) | — |
|
|
243
|
+
| **WiFi** | None | `WiFi` class | New |
|
|
244
|
+
| **Touch** | `TouchSensor` (external TTP223) | + `CapTouch` (ESP32 built-in touch peripheral) | New |
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 5. Key `_core.py` Changes
|
|
249
|
+
|
|
250
|
+
### 5-A. `PWMOutputDevice` — Channel restriction removed
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
# picozero: PIN_TO_PWM_CHANNEL table + _check_pwm_channel() → removed
|
|
254
|
+
# espzero: ESP32 LEDC assigns each pin an independent channel — no conflicts
|
|
255
|
+
|
|
256
|
+
class PWMOutputDevice(OutputDevice, PinMixin):
|
|
257
|
+
def __init__(self, pin, freq=None, duty_factor=65535, ...):
|
|
258
|
+
self._pwm = _hal.make_pwm(pin, freq) # routed through HAL
|
|
259
|
+
|
|
260
|
+
def _write(self, value):
|
|
261
|
+
_hal.set_duty_u16(self._pwm, self._value_to_state(value)) # with fallback
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 5-B. `AnalogInputDevice` — ADC scale correction
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
class AnalogInputDevice(InputDevice, PinMixin):
|
|
268
|
+
def __init__(self, pin, ...):
|
|
269
|
+
self._adc = _hal.make_adc(pin) # attenuation applied in profile
|
|
270
|
+
|
|
271
|
+
def _read(self):
|
|
272
|
+
raw_u16 = _hal.adc_read_u16(self._adc) # scaled to 0–65535
|
|
273
|
+
return self._state_to_value(raw_u16) # upper logic unchanged
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def voltage(self):
|
|
277
|
+
return self.value * _hal.get_profile().ADC_VREF
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 5-C. Built-in objects renamed
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
# picozero: pico_led, pico_temp_sensor
|
|
284
|
+
# espzero:
|
|
285
|
+
esp_led = LED("internal") # profile resolves "internal" alias
|
|
286
|
+
esp_temp_sensor = ESPTemperatureSensor() # uses esp32.raw_temperature()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 6. ESP32-Specific Classes
|
|
292
|
+
|
|
293
|
+
### 6-A. `WiFi` (`_wifi.py`)
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from espzero import WiFi
|
|
297
|
+
|
|
298
|
+
wifi = WiFi()
|
|
299
|
+
ip = wifi.connect("MySSID", "password", timeout=10)
|
|
300
|
+
print("Connected:", ip) # blocks until connected or raises OSError
|
|
301
|
+
|
|
302
|
+
wifi.scan() # returns list of nearby APs
|
|
303
|
+
wifi.is_connected # True / False
|
|
304
|
+
wifi.ip # current IP string
|
|
305
|
+
wifi.disconnect()
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
> **Note:** After `connect()`, the HAL sets `_wifi_active = True` automatically.
|
|
309
|
+
> Any attempt to use an ADC2 pin after this will print a warning.
|
|
310
|
+
|
|
311
|
+
### 6-B. `CapTouch` — Capacitive touch (`_touch.py`)
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
from espzero import CapTouch
|
|
315
|
+
|
|
316
|
+
touch = CapTouch(pin=4, threshold=300) # GPIO 4 = T0 on WROOM
|
|
317
|
+
if touch.is_touched:
|
|
318
|
+
print("Touched! Raw:", touch.value)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Unlike `TouchSensor` (which wraps an external TTP223 IC), `CapTouch` uses the ESP32's built-in capacitive touch peripheral directly. Available on pins T0–T9 of WROOM/WROVER modules.
|
|
322
|
+
|
|
323
|
+
### 6-C. `NeoPixelLED` — Built-in RGB LED wrapper (`_core.py`)
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
class NeoPixelLED:
|
|
327
|
+
"""
|
|
328
|
+
Treats a single WS2812 pixel as a simple on/off LED.
|
|
329
|
+
Automatically used as esp_led on boards where INTERNAL_LED_TYPE == 'neopixel'
|
|
330
|
+
(e.g. ESP32-S3 DevKit GPIO 48, M5Stack ATOM GPIO 27).
|
|
331
|
+
"""
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## 7. Safety Nets for Beginners
|
|
337
|
+
|
|
338
|
+
espzero includes three runtime warnings designed to save beginners from common hardware pitfalls:
|
|
339
|
+
|
|
340
|
+
| # | Trigger | Warning |
|
|
341
|
+
|---|---------|---------|
|
|
342
|
+
| 1 | Using an **ADC2 pin while WiFi is active** | `[espzero] WARNING: WiFi is active. ADC2 (GPIO N) cannot be used...` |
|
|
343
|
+
| 2 | Attaching a **button to a strapping pin** | `[espzero] WARNING: GPIO N is a strapping pin. Boot issues may occur.` |
|
|
344
|
+
| 3 | Running on **firmware < 1.19** (no `duty_u16`) | Silently falls back to `duty()` — no crash |
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 8. Supported Boards
|
|
349
|
+
|
|
350
|
+
| Board | Built-in LED | ADC1 Pins | ADC2 Pins* | Touch Pins |
|
|
351
|
+
|-------|-------------|-----------|------------|------------|
|
|
352
|
+
| ESP32 DevKit V1 (WROOM) | GPIO 2 (active-low) | 32–39 | 0,2,4,12–15,25–27 | T0(4)–T9(32) |
|
|
353
|
+
| ESP32-S3 DevKit | GPIO 48 (RGB) | 1–10 | 11–20 | T1–T14 |
|
|
354
|
+
| ESP32-C3 Mini | GPIO 8 (active-low) | 0–4 | None | None |
|
|
355
|
+
| M5Stack ATOM Lite | GPIO 27 (RGB) | 33,35,36 | 0,2,4,12–15,25–27 | T0(4),T3(15) |
|
|
356
|
+
| Wemos D1 Mini32 | GPIO 2 (active-low) | 32–39 | 0,2,4,12–15,25–27 | T0(4)–T9(32) |
|
|
357
|
+
| NodeMCU V3 Lolin (ESP8266) | GPIO 2 (active-low) | A0 only (10-bit, 0–1.0 V) | None | None |
|
|
358
|
+
|
|
359
|
+
> \* ADC2 pins cannot be used while WiFi is active. For educational use, stick to ADC1 pins only.
|
|
360
|
+
>
|
|
361
|
+
> **ESP8266 ADC note:** A0 accepts 0–1.0 V only. Use a voltage divider (e.g. 220 kΩ + 100 kΩ) to measure 3.3 V signals safely.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## 9. Implementation Roadmap
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
Phase 1 — Core port (complete)
|
|
369
|
+
[x] profiles/_base.py — BoardProfile abstract base class
|
|
370
|
+
[x] profiles/esp32_boards.py — WROOM, S3, C3, M5Stack, Wemos profiles
|
|
371
|
+
[x] profiles/auto.py — Runtime auto-detection
|
|
372
|
+
[x] _hal.py — HAL wrappers
|
|
373
|
+
[x] _core.py — Ported picozero core logic
|
|
374
|
+
· PWMOutputDevice: removed channel-collision check
|
|
375
|
+
· AnalogInputDevice: ADC read routed through HAL
|
|
376
|
+
· pico_led / pico_temp_sensor → esp_led / esp_temp_sensor
|
|
377
|
+
[x] __init__.py — begin() + public API
|
|
378
|
+
|
|
379
|
+
Phase 2 — ESP32-specific features (complete)
|
|
380
|
+
[x] _wifi.py — WiFi class
|
|
381
|
+
[x] _touch.py — CapTouch class
|
|
382
|
+
[x] Additional board profiles — S3, C3, M5Stack, Wemos
|
|
383
|
+
|
|
384
|
+
Phase 3 — Mu Editor integration (complete)
|
|
385
|
+
[x] mu/resources/esp32/ — Library bundled with Mu Editor
|
|
386
|
+
[x] mu/logic.py — esp32_lib auto-provisioned to mu_code/
|
|
387
|
+
[x] mu/interface/editor.py — Jedi search path includes esp32_lib
|
|
388
|
+
(dynamic autocomplete via source analysis)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## 10. Design Decisions
|
|
394
|
+
|
|
395
|
+
| # | Topic | Decision |
|
|
396
|
+
|---|-------|----------|
|
|
397
|
+
| 1 | **ADC2 + WiFi warning** | `_hal.make_adc()` checks `_wifi_active` flag and prints a warning |
|
|
398
|
+
| 2 | **`duty_u16()` fallback** | `_hal.set_duty_u16()` wrapper silently falls back to `duty(val>>6)` on firmware < 1.19 |
|
|
399
|
+
| 3 | **Strapping pin warning** | `make_digital_in()` checks `STRAPPING_PINS` and prints a warning |
|
|
400
|
+
| 4 | **NeoPixel built-in LED** | `INTERNAL_LED_TYPE = "neopixel"` profile field selects `NeoPixelLED` wrapper automatically |
|
|
401
|
+
| 5 | **Lazy profile loading** | `_PROFILE_MAP` dict + `importlib` — only the selected board's module is imported |
|
|
402
|
+
| 6 | **Autocomplete** | Jedi analyses live source in `mu/resources/esp32/` — no separate static API file needed |
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
MIT License — contributions welcome.
|
|
409
|
+
This library is part of the [Mu Editor](https://github.com/roboticsware/mu) project for educational IoT programming.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
profiles/__init__.py,sha256=mDXtgRSz1isBGjbGM38SCNDtDIium3KoXxEkO3LQzd8,260
|
|
2
|
+
profiles/_base.py,sha256=Qpt_GZyuUWxfbQ-SaCpV2tICVuRtqcnCwUdDdsSsP-Q,4345
|
|
3
|
+
profiles/auto.py,sha256=r5k6XtuWuJ4NwnGNqTmnWF60Liow_quupyuZLVIApLY,2131
|
|
4
|
+
profiles/esp32_boards.py,sha256=rrtHsULIH0TnTOjMXgCIyEehS94QitfcBEmJDRky1_o,8215
|
|
5
|
+
espzero-0.0.1.dist-info/METADATA,sha256=7a9lJHtNibaYiXWXySKdZj2vqNbeqjGqd1GD70dy_hE,14298
|
|
6
|
+
espzero-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
espzero-0.0.1.dist-info/top_level.txt,sha256=7AbhkLEJOzoiSyeUoPAwBlWoZ1vvIiBehEFDT3dsxpQ,9
|
|
8
|
+
espzero-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
profiles
|
profiles/__init__.py
ADDED
profiles/_base.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# This file is part of espzero, automatically synced from
|
|
2
|
+
# https://github.com/roboticsware/espzero at revision 24cae2bc1c028458052675181e95f2a08fdf7481.
|
|
3
|
+
# Do not edit this file directly — run update_user_libs.py espzero instead.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
espzero/profiles/_base.py
|
|
7
|
+
Abstract base class for all board profiles.
|
|
8
|
+
|
|
9
|
+
Every board-specific profile inherits from BoardProfile and declares
|
|
10
|
+
its hardware constants (pin aliases, ADC settings, strapping pins, etc.).
|
|
11
|
+
The HAL and core logic read these constants at runtime so that no
|
|
12
|
+
board-specific code ever leaks into _core.py.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BoardProfile:
|
|
17
|
+
"""Abstract base class for all board profiles."""
|
|
18
|
+
|
|
19
|
+
NAME = "unknown" # Human-readable board name
|
|
20
|
+
CHIP = "esp32" # Chip family: esp32 / esp32s3 / esp32c3 / esp8266
|
|
21
|
+
|
|
22
|
+
# ── Pin aliases ───────────────────────────────────────────────
|
|
23
|
+
# Allows users to refer to pins by name (e.g. "internal", "led")
|
|
24
|
+
# instead of raw GPIO numbers.
|
|
25
|
+
PIN_ALIASES = {}
|
|
26
|
+
|
|
27
|
+
# ── ADC settings ──────────────────────────────────────────────
|
|
28
|
+
ADC_MAX_RAW = 4095 # 12-bit resolution (0–4095)
|
|
29
|
+
ADC_SCALE = 65535 # Target scale for picozero compatibility (read_u16)
|
|
30
|
+
ADC_ATTEN = None # e.g. machine.ADC.ATTN_11DB; None = use firmware default
|
|
31
|
+
ADC_VREF = 3.3 # Maximum input voltage (matches attenuation setting)
|
|
32
|
+
|
|
33
|
+
# ── PWM settings ──────────────────────────────────────────────
|
|
34
|
+
PWM_DEFAULT_FREQ = 1000 # Default PWM frequency in Hz (Pico used 100 Hz)
|
|
35
|
+
PWM_DUTY_MAX = 65535 # Internal duty scale is always 16-bit
|
|
36
|
+
SERVO_FREQ = 50 # Standard servo frequency: 50 Hz (20 ms frame)
|
|
37
|
+
|
|
38
|
+
# ── Built-in temperature sensor ───────────────────────────────
|
|
39
|
+
INTERNAL_TEMP_AVAILABLE = False
|
|
40
|
+
|
|
41
|
+
# ── Built-in LED type ─────────────────────────────────────────
|
|
42
|
+
# "digital" – standard GPIO LED (on/off)
|
|
43
|
+
# "neopixel" – WS2812 RGB LED (e.g. ESP32-S3 DevKit, M5Stack ATOM)
|
|
44
|
+
INTERNAL_LED_TYPE = "digital"
|
|
45
|
+
INTERNAL_LED_ACTIVE_HIGH = True
|
|
46
|
+
|
|
47
|
+
# ── Boot strapping pins ───────────────────────────────────────
|
|
48
|
+
# Connecting a button or switch to these pins may cause boot failures
|
|
49
|
+
# or force the chip into download mode.
|
|
50
|
+
STRAPPING_PINS = [] # Example: [0, 2, 5, 12, 15]
|
|
51
|
+
|
|
52
|
+
# ── ADC2 pins (unavailable while WiFi is active) ──────────────
|
|
53
|
+
ADC2_PINS = [] # Example: [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
54
|
+
|
|
55
|
+
# ── Restricted pins (wired to internal flash — never use as GPIO) ──
|
|
56
|
+
# Driving these pins will corrupt flash memory and hang the system.
|
|
57
|
+
RESTRICTED_PINS = [] # Example: [6, 7, 8, 9, 10, 11]
|
|
58
|
+
|
|
59
|
+
# ─────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def resolve_pin(self, pin):
|
|
62
|
+
"""Resolve a string alias or integer to a GPIO number."""
|
|
63
|
+
if isinstance(pin, str):
|
|
64
|
+
if pin in self.PIN_ALIASES:
|
|
65
|
+
return self.PIN_ALIASES[pin]
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Unknown pin alias '{}' for board '{}'. "
|
|
68
|
+
"Available aliases: {}".format(pin, self.NAME, list(self.PIN_ALIASES.keys()))
|
|
69
|
+
)
|
|
70
|
+
return int(pin)
|
|
71
|
+
|
|
72
|
+
def make_adc(self, gpio_num):
|
|
73
|
+
"""Create an ADC object for the given GPIO, applying attenuation if configured."""
|
|
74
|
+
from machine import ADC, Pin
|
|
75
|
+
adc = ADC(Pin(gpio_num))
|
|
76
|
+
if self.ADC_ATTEN is not None:
|
|
77
|
+
adc.atten(self.ADC_ATTEN)
|
|
78
|
+
return adc
|
|
79
|
+
|
|
80
|
+
def adc_read_u16(self, adc_obj):
|
|
81
|
+
"""Read raw 12-bit ADC value and scale it to 0–65535 for picozero compatibility."""
|
|
82
|
+
raw = adc_obj.read() # 0–4095
|
|
83
|
+
return (raw * self.ADC_SCALE) // self.ADC_MAX_RAW
|
profiles/auto.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# This file is part of espzero, automatically synced from
|
|
2
|
+
# https://github.com/roboticsware/espzero at revision 24cae2bc1c028458052675181e95f2a08fdf7481.
|
|
3
|
+
# Do not edit this file directly — run update_user_libs.py espzero instead.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
espzero/profiles/auto.py
|
|
7
|
+
Runtime board auto-detection.
|
|
8
|
+
|
|
9
|
+
Inspects sys.implementation._machine to identify the chip and returns
|
|
10
|
+
the matching board profile key used in espzero._PROFILE_MAP.
|
|
11
|
+
"""
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def detect():
|
|
16
|
+
"""
|
|
17
|
+
Detect the current board at runtime by inspecting sys.implementation._machine.
|
|
18
|
+
|
|
19
|
+
Returns a board key string that corresponds to an entry in
|
|
20
|
+
espzero._PROFILE_MAP (e.g. 'esp32_38pin_nodemcu').
|
|
21
|
+
Falls back to 'esp32_38pin_nodemcu' (38-pin NodeMCU) if detection fails.
|
|
22
|
+
|
|
23
|
+
Note: ESP32 DevKit V1 (30-pin) and ESP32 38-Pin NodeMCU share the same
|
|
24
|
+
WROOM-32 chip and firmware, so they cannot be distinguished at runtime.
|
|
25
|
+
The 38-pin NodeMCU is used as the generic ESP32 fallback because:
|
|
26
|
+
1. It is the more widely available board in the target region.
|
|
27
|
+
2. Its profile includes RESTRICTED_PINS [6-11] safety protection.
|
|
28
|
+
If your board is a 30-pin DevKit V1, call espzero.begin('esp32_devkit_v1')
|
|
29
|
+
explicitly to use its profile (active-low LED, no RESTRICTED_PINS).
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
machine_str = sys.implementation._machine.lower()
|
|
33
|
+
except AttributeError:
|
|
34
|
+
machine_str = ""
|
|
35
|
+
|
|
36
|
+
if "esp32s3" in machine_str or "esp32-s3" in machine_str:
|
|
37
|
+
return "esp32_s3_devkit"
|
|
38
|
+
elif "esp32c3" in machine_str or "esp32-c3" in machine_str:
|
|
39
|
+
return "esp32_c3_mini"
|
|
40
|
+
elif "esp8266" in machine_str:
|
|
41
|
+
return "esp8266_lolin_v3"
|
|
42
|
+
elif "esp32" in machine_str:
|
|
43
|
+
# Cannot distinguish 38-pin NodeMCU from DevKit V1 — same WROOM-32 chip.
|
|
44
|
+
# Default to 38-pin NodeMCU (more common; includes flash-pin safety guard).
|
|
45
|
+
return "esp32_38pin_nodemcu"
|
|
46
|
+
else:
|
|
47
|
+
print("[espzero] WARNING: Could not auto-detect board. "
|
|
48
|
+
"Falling back to 'esp32_38pin_nodemcu' profile. "
|
|
49
|
+
"Call espzero.begin('<board_name>') to specify your board.")
|
|
50
|
+
return "esp32_38pin_nodemcu"
|
profiles/esp32_boards.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""
|
|
2
|
+
espzero/profiles/esp32_boards.py
|
|
3
|
+
Board profile definitions for common ESP32-family boards.
|
|
4
|
+
|
|
5
|
+
Each class declares the hardware constants for one board variant.
|
|
6
|
+
Add new boards here by subclassing BoardProfile and overriding the
|
|
7
|
+
relevant fields.
|
|
8
|
+
"""
|
|
9
|
+
from ._base import BoardProfile
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# --- ADC attenuation helper -------------------------------------
|
|
13
|
+
# Imported lazily so the profiles module can be parsed on the host
|
|
14
|
+
# (e.g. for IDE support) without requiring machine to be available.
|
|
15
|
+
def _atten():
|
|
16
|
+
try:
|
|
17
|
+
from machine import ADC
|
|
18
|
+
return ADC.ATTN_11DB
|
|
19
|
+
except Exception:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# --------------------------------------------------------------
|
|
24
|
+
# ESP32 DevKit V1 / WROOM-32
|
|
25
|
+
# --------------------------------------------------------------
|
|
26
|
+
class ESP32DevKitV1(BoardProfile):
|
|
27
|
+
"""
|
|
28
|
+
Espressif ESP32 DevKit V1 (WROOM-32).
|
|
29
|
+
Built-in LED: GPIO 2 (active-low, blue).
|
|
30
|
+
ADC1: GPIO 32-39 ADC2: GPIO 0, 2, 4, 12-15, 25-27 (unusable with WiFi)
|
|
31
|
+
Strapping pins: GPIO 0, 2, 5, 12, 15
|
|
32
|
+
"""
|
|
33
|
+
NAME = "esp32_devkit_v1"
|
|
34
|
+
CHIP = "esp32"
|
|
35
|
+
|
|
36
|
+
PIN_ALIASES = {
|
|
37
|
+
"internal": 2,
|
|
38
|
+
"led": 2,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ADC_ATTEN = _atten()
|
|
42
|
+
ADC_VREF = 3.6
|
|
43
|
+
INTERNAL_LED_TYPE = "digital"
|
|
44
|
+
INTERNAL_LED_ACTIVE_HIGH = False # active-low
|
|
45
|
+
STRAPPING_PINS = [0, 2, 5, 12, 15]
|
|
46
|
+
ADC2_PINS = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# --------------------------------------------------------------
|
|
50
|
+
# ESP32-S3 DevKit-C
|
|
51
|
+
# --------------------------------------------------------------
|
|
52
|
+
class ESP32S3DevKit(BoardProfile):
|
|
53
|
+
"""
|
|
54
|
+
Espressif ESP32-S3 DevKitC-1.
|
|
55
|
+
Built-in LED: GPIO 48 (WS2812 NeoPixel RGB).
|
|
56
|
+
ADC1: GPIO 1-10 ADC2: GPIO 11-20 (unusable with WiFi)
|
|
57
|
+
Strapping pins: GPIO 0, 3, 45, 46
|
|
58
|
+
"""
|
|
59
|
+
NAME = "esp32_s3_devkit"
|
|
60
|
+
CHIP = "esp32s3"
|
|
61
|
+
|
|
62
|
+
PIN_ALIASES = {
|
|
63
|
+
"internal": 48,
|
|
64
|
+
"led": 48,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ADC_ATTEN = _atten()
|
|
68
|
+
ADC_VREF = 3.6
|
|
69
|
+
INTERNAL_LED_TYPE = "neopixel"
|
|
70
|
+
INTERNAL_LED_ACTIVE_HIGH = True
|
|
71
|
+
STRAPPING_PINS = [0, 3, 45, 46]
|
|
72
|
+
ADC2_PINS = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# --------------------------------------------------------------
|
|
76
|
+
# ESP32-C3 Mini / SuperMini
|
|
77
|
+
# --------------------------------------------------------------
|
|
78
|
+
class ESP32C3Mini(BoardProfile):
|
|
79
|
+
"""
|
|
80
|
+
ESP32-C3 Mini / SuperMini.
|
|
81
|
+
Built-in LED: GPIO 8 (active-low).
|
|
82
|
+
ADC1 only: GPIO 0-4. No ADC2 channel.
|
|
83
|
+
Strapping pins: GPIO 2, 8, 9
|
|
84
|
+
"""
|
|
85
|
+
NAME = "esp32_c3_mini"
|
|
86
|
+
CHIP = "esp32c3"
|
|
87
|
+
|
|
88
|
+
PIN_ALIASES = {
|
|
89
|
+
"internal": 8,
|
|
90
|
+
"led": 8,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ADC_ATTEN = _atten()
|
|
94
|
+
ADC_VREF = 3.6
|
|
95
|
+
INTERNAL_LED_TYPE = "digital"
|
|
96
|
+
INTERNAL_LED_ACTIVE_HIGH = False # active-low
|
|
97
|
+
STRAPPING_PINS = [2, 8, 9]
|
|
98
|
+
ADC2_PINS = [] # C3 has no ADC2
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# --------------------------------------------------------------
|
|
102
|
+
# M5Stack ATOM Lite / Matrix
|
|
103
|
+
# --------------------------------------------------------------
|
|
104
|
+
class M5StackAtom(BoardProfile):
|
|
105
|
+
"""
|
|
106
|
+
M5Stack ATOM Lite / Matrix.
|
|
107
|
+
Built-in LED: GPIO 27 (WS2812 NeoPixel).
|
|
108
|
+
Built-in button: GPIO 39 (G39).
|
|
109
|
+
"""
|
|
110
|
+
NAME = "m5stack_atom"
|
|
111
|
+
CHIP = "esp32"
|
|
112
|
+
|
|
113
|
+
PIN_ALIASES = {
|
|
114
|
+
"internal": 27,
|
|
115
|
+
"led": 27,
|
|
116
|
+
"btn": 39, # Built-in button (G39)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
ADC_ATTEN = _atten()
|
|
120
|
+
ADC_VREF = 3.6
|
|
121
|
+
INTERNAL_LED_TYPE = "neopixel"
|
|
122
|
+
INTERNAL_LED_ACTIVE_HIGH = True
|
|
123
|
+
STRAPPING_PINS = [0, 2, 5, 12, 15]
|
|
124
|
+
ADC2_PINS = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# --------------------------------------------------------------
|
|
128
|
+
# Wemos D1 Mini32 (ESP32)
|
|
129
|
+
# --------------------------------------------------------------
|
|
130
|
+
class WemosD1Mini32(BoardProfile):
|
|
131
|
+
"""Wemos / LOLIN D1 Mini32."""
|
|
132
|
+
NAME = "wemos_d1_mini32"
|
|
133
|
+
CHIP = "esp32"
|
|
134
|
+
|
|
135
|
+
PIN_ALIASES = {
|
|
136
|
+
"internal": 2,
|
|
137
|
+
"led": 2,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ADC_ATTEN = _atten()
|
|
141
|
+
ADC_VREF = 3.6
|
|
142
|
+
INTERNAL_LED_TYPE = "digital"
|
|
143
|
+
INTERNAL_LED_ACTIVE_HIGH = False
|
|
144
|
+
STRAPPING_PINS = [0, 2, 5, 12, 15]
|
|
145
|
+
ADC2_PINS = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# --------------------------------------------------------------
|
|
149
|
+
# NodeMCU V3 Lolin (ESP8266)
|
|
150
|
+
# --------------------------------------------------------------
|
|
151
|
+
class ESP8266LolinV3(BoardProfile):
|
|
152
|
+
"""
|
|
153
|
+
NodeMCU V3 Lolin (ESP8266).
|
|
154
|
+
Built-in LED: GPIO 2 (active-low, blue).
|
|
155
|
+
Single ADC: A0 only — 10-bit (0-1023).
|
|
156
|
+
The Lolin V3 board includes an on-board voltage divider (220 kΩ / 100 kΩ)
|
|
157
|
+
that extends the A0 input range from the bare ESP8266's 0-1.0 V to 0-3.3 V.
|
|
158
|
+
ADC_VREF is therefore 3.3 V for this board.
|
|
159
|
+
D-pin labels on the silkscreen differ from GPIO numbers;
|
|
160
|
+
use the D0-D8 aliases so user code matches the board labels.
|
|
161
|
+
Standard I2C: SDA=D2 (GPIO 4), SCL=D1 (GPIO 5).
|
|
162
|
+
No ADC2 group, no TouchPad, no ATTN setting.
|
|
163
|
+
"""
|
|
164
|
+
NAME = "esp8266_lolin_v3"
|
|
165
|
+
CHIP = "esp8266"
|
|
166
|
+
|
|
167
|
+
PIN_ALIASES = {
|
|
168
|
+
# Silkscreen label → GPIO number
|
|
169
|
+
"D0": 16, "D1": 5, "D2": 4, "D3": 0,
|
|
170
|
+
"D4": 2, "D5": 14, "D6": 12, "D7": 13,
|
|
171
|
+
"D8": 15, "RX": 3, "TX": 1,
|
|
172
|
+
# Analog pin (the only ADC input)
|
|
173
|
+
"A0": 0,
|
|
174
|
+
# Built-in LED aliases
|
|
175
|
+
"internal": 2,
|
|
176
|
+
"led": 2,
|
|
177
|
+
# Standard I2C bus (matches Arduino/MicroPython convention for Lolin V3)
|
|
178
|
+
"sda": 4, # D2
|
|
179
|
+
"scl": 5, # D1
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# 10-bit ADC (0-1023). Lolin V3 on-board divider extends range to 3.3 V.
|
|
183
|
+
ADC_MAX_RAW = 1023
|
|
184
|
+
ADC_ATTEN = None # ESP8266 has no attenuation register
|
|
185
|
+
ADC_VREF = 3.3 # 0–3.3 V thanks to the Lolin V3 voltage divider
|
|
186
|
+
|
|
187
|
+
INTERNAL_LED_TYPE = "digital"
|
|
188
|
+
INTERNAL_LED_ACTIVE_HIGH = False # active-low
|
|
189
|
+
|
|
190
|
+
# ESP8266 boots from GPIO 0/2/15 — warn users who attach buttons there
|
|
191
|
+
STRAPPING_PINS = [0, 2, 15]
|
|
192
|
+
|
|
193
|
+
# ESP8266 has only one ADC channel; WiFi/ADC conflict exists but is
|
|
194
|
+
# different from ESP32 (cannot read ADC during WiFi TX bursts).
|
|
195
|
+
ADC2_PINS = []
|
|
196
|
+
|
|
197
|
+
def make_adc(self, gpio_num):
|
|
198
|
+
"""
|
|
199
|
+
ESP8266 ADC: only one channel exists, always accessed as ADC(0).
|
|
200
|
+
The gpio_num argument is accepted for API compatibility but ignored.
|
|
201
|
+
"""
|
|
202
|
+
from machine import ADC
|
|
203
|
+
return ADC(0) # channel 0 = A0 pin, the sole ADC input on ESP8266
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# --------------------------------------------------------------
|
|
207
|
+
# ESP32 38-Pin NodeMCU (Type-C / Micro-USB combo)
|
|
208
|
+
# --------------------------------------------------------------
|
|
209
|
+
class ESP32_38Pin_NodeMCU(BoardProfile):
|
|
210
|
+
"""
|
|
211
|
+
ESP32 38-Pin NodeMCU development board (Type-C & Micro-USB combo).
|
|
212
|
+
|
|
213
|
+
Built-in LED: GPIO 1 (TX pin — lit during communication, but works as GPIO).
|
|
214
|
+
I2C defaults: SDA=GPIO 21, SCL=GPIO 22.
|
|
215
|
+
BOOT button: GPIO 0.
|
|
216
|
+
|
|
217
|
+
GPIO 6-11 are internally wired to the SPI flash memory.
|
|
218
|
+
Driving them as GPIO will corrupt flash and hang the system.
|
|
219
|
+
Safe user GPIOs: 1-5, 12-23, 25-27, 32-39
|
|
220
|
+
(34, 35, 36, 39 are input-only — no internal pull-up/down).
|
|
221
|
+
|
|
222
|
+
ADC1: GPIO 32-39 ADC2: GPIO 0, 2, 4, 12-15, 25-27 (unusable with WiFi)
|
|
223
|
+
Strapping pins: GPIO 0, 2, 5, 12, 15
|
|
224
|
+
"""
|
|
225
|
+
NAME = "esp32_38pin_nodemcu"
|
|
226
|
+
CHIP = "esp32"
|
|
227
|
+
|
|
228
|
+
PIN_ALIASES = {
|
|
229
|
+
# Built-in LED (Mapped to GPIO 1 TX pin as per user test)
|
|
230
|
+
"internal": 1,
|
|
231
|
+
"led": 1,
|
|
232
|
+
"builtin_led": 1,
|
|
233
|
+
# Built-in BOOT button
|
|
234
|
+
"button": 0,
|
|
235
|
+
# Standard I2C bus
|
|
236
|
+
"sda": 21,
|
|
237
|
+
"scl": 22,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
ADC_ATTEN = _atten() # ATTN_11DB → 0–3.6 V range
|
|
241
|
+
ADC_VREF = 3.6
|
|
242
|
+
|
|
243
|
+
# This board's built-in LED is active-LOW (0 = on)
|
|
244
|
+
INTERNAL_LED_TYPE = "digital"
|
|
245
|
+
INTERNAL_LED_ACTIVE_HIGH = False
|
|
246
|
+
|
|
247
|
+
# Boot strapping pins — attaching buttons here may cause boot failures
|
|
248
|
+
STRAPPING_PINS = [0, 2, 5, 12, 15]
|
|
249
|
+
|
|
250
|
+
# ADC2 pins — cannot be read while WiFi is active
|
|
251
|
+
ADC2_PINS = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27]
|
|
252
|
+
|
|
253
|
+
# Flash-memory pins — NEVER use as GPIO; doing so corrupts flash
|
|
254
|
+
RESTRICTED_PINS = [6, 7, 8, 9, 10, 11]
|