dln2 0.2.0__tar.gz → 0.2.2__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: dln2
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Unified DLN2 driver — SPI, GPIO, I2C, ADC over a single USB connection. Originally developed for the Pico USB I/O Board.
5
5
  Home-page: https://github.com/syabyr/dln2_wrapper
6
6
  Author: IPM Group
@@ -21,17 +21,6 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: pyusb>=1.2
24
- Dynamic: author
25
- Dynamic: author-email
26
- Dynamic: classifier
27
- Dynamic: description
28
- Dynamic: description-content-type
29
- Dynamic: home-page
30
- Dynamic: license
31
- Dynamic: license-file
32
- Dynamic: requires-dist
33
- Dynamic: requires-python
34
- Dynamic: summary
35
24
 
36
25
  # dln2 — Unified DLN2 USB I/O Board Wrapper
37
26
 
@@ -118,10 +107,6 @@ smbus-compatible API: `write_byte()`, `read_byte_data()`,
118
107
  `read_channel(channel)`, `read_all()`, `read_volts(channel)`,
119
108
  `set_resolution(bits)`, `enable_channel(ch)`, `disable_channel(ch)`
120
109
 
121
- ### BME280
122
- `read_chip_id()`, `get_temperature()`, `get_humidity()`, `get_pressure()`,
123
- `get_altitude()`, `get_all()`
124
-
125
110
  ## CLI Commands
126
111
 
127
112
  After installation, the following commands are available:
@@ -135,7 +120,6 @@ After installation, the following commands are available:
135
120
  | `dln2-adc-watch` | Stream one ADC channel |
136
121
  | `dln2-i2c-scan` | Scan I2C bus for devices |
137
122
  | `dln2-i2c-test` | Read/write I2C register |
138
- | `dln2-bme280` | Read BME280 sensor measurements |
139
123
  | `dln2-spi-test` | Send SPI JEDEC ID probe |
140
124
  | `dln2-bpw-test` | Cycle SPI bits-per-word 4..16 |
141
125
 
@@ -153,28 +137,59 @@ dln2-adc-watch --channel 0 --interval 0.5
153
137
  dln2-i2c-scan
154
138
  dln2-i2c-test --address 0x76 --register 0xD0 --read 1
155
139
 
156
- # BME280
157
- dln2-bme280 --address 0x76 --pretty
158
-
159
140
  # SPI
160
141
  dln2-spi-test
161
142
  dln2-bpw-test
162
143
  ```
163
144
 
164
- ## Examples (as scripts)
145
+ ## Examples
146
+
147
+ ### Core examples (this repo)
148
+
149
+ Basic I/O operations that exercise the wrapper itself:
165
150
 
166
151
  ```bash
167
152
  # SPI JEDEC ID read
168
153
  python3 examples/spidev_test.py
169
154
 
155
+ # SPI bits-per-word cycle
156
+ python3 examples/bpw_tester.py
157
+
170
158
  # GPIO pin toggle (default: Pico LED)
171
159
  python3 examples/gpio_toggle.py --pin 25
172
160
 
161
+ # GPIO pin info
162
+ python3 examples/gpio_info.py --pin 2
163
+
164
+ # GPIO event watch
165
+ python3 examples/gpio_watch.py --pin 2
166
+
173
167
  # I2C bus scan
174
168
  python3 examples/i2c_scan.py
175
169
 
176
- # ADC monitor
170
+ # I2C register read/write
171
+ python3 examples/i2c_test.py --address 0x76 --register 0xD0 --read 1
172
+
173
+ # ADC read
177
174
  python3 examples/adc_info.py
175
+
176
+ # ADC stream
177
+ python3 examples/adc_watch.py --channel 0
178
+ ```
179
+
180
+ ### Device-specific examples (separate repo)
181
+
182
+ Sensor test scripts live in [dln2_examples](https://github.com/syabyr/dln2_examples):
183
+
184
+ ```bash
185
+ git clone https://github.com/syabyr/dln2_examples
186
+ cd dln2_examples/i2c
187
+
188
+ python3 bh1750_full_test.py # BH1750 ambient light @ 0x23
189
+ python3 bme280_full_test.py # BME280 temp/press/humid @ 0x76
190
+ python3 bmp180_full_test.py # BMP180 temp/pressure @ 0x77
191
+ python3 tsl2561_full_test.py # TSL2561 ambient light @ 0x39
192
+ python3 mpu9250_full_test.py # MPU9250 9-axis IMU @ 0x68
178
193
  ```
179
194
 
180
195
  ## Why Unified?
@@ -187,10 +202,31 @@ python3 examples/adc_info.py
187
202
  | Multi-device | Manual USB enumeration | `list_devices()` |
188
203
  | Cross-module data flow | Impossible (4 echo counters) | Trivial (same counter) |
189
204
 
205
+ ## Pin Constraints
206
+
207
+ | Pins | Capability |
208
+ |---|---|
209
+ | 0–22, 26–28 | Full GPIO (input/output + events) |
210
+ | 23, 24, 29 | Output-only (reserved for SMPS / VBUS / VSYS) |
211
+ | 25 | Output-only (on-board LED) |
212
+ | ADC 0–2 | Map to GPIO 26, 27, 28 respectively |
213
+
214
+ ## Firmware Compatibility
215
+
216
+ | dln2 Python | Minimum firmware | Notes |
217
+ |---|---|---|
218
+ | 0.2.1 | [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) `>= v0.2` | SPI bpw validation, ADC pin-sharing fix |
219
+ | 0.2.0 | any | Works with all firmware versions |
220
+
221
+ To flash updated firmware:
222
+ 1. Hold the BOOTSEL button while connecting the Pico (or press and release RESET while holding BOOTSEL)
223
+ 2. Copy `dln2.uf2` to the `RPI-RP2` USB drive that appears
224
+ 3. The Pico reboots automatically with the new firmware
225
+
190
226
  ## Related
191
227
 
192
228
  - [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) — RP2040 firmware
193
- - [st7789 display driver](https://github.com/syabyr/dln2_wrapper) — example ST7789 + BME280 application
229
+ - [dln2_examples](https://github.com/syabyr/dln2_examples) — sensor test scripts (BH1750, BME280, BMP180, TSL2561, MPU9250, …)
194
230
 
195
231
  ## License
196
232
 
@@ -83,10 +83,6 @@ smbus-compatible API: `write_byte()`, `read_byte_data()`,
83
83
  `read_channel(channel)`, `read_all()`, `read_volts(channel)`,
84
84
  `set_resolution(bits)`, `enable_channel(ch)`, `disable_channel(ch)`
85
85
 
86
- ### BME280
87
- `read_chip_id()`, `get_temperature()`, `get_humidity()`, `get_pressure()`,
88
- `get_altitude()`, `get_all()`
89
-
90
86
  ## CLI Commands
91
87
 
92
88
  After installation, the following commands are available:
@@ -100,7 +96,6 @@ After installation, the following commands are available:
100
96
  | `dln2-adc-watch` | Stream one ADC channel |
101
97
  | `dln2-i2c-scan` | Scan I2C bus for devices |
102
98
  | `dln2-i2c-test` | Read/write I2C register |
103
- | `dln2-bme280` | Read BME280 sensor measurements |
104
99
  | `dln2-spi-test` | Send SPI JEDEC ID probe |
105
100
  | `dln2-bpw-test` | Cycle SPI bits-per-word 4..16 |
106
101
 
@@ -118,28 +113,59 @@ dln2-adc-watch --channel 0 --interval 0.5
118
113
  dln2-i2c-scan
119
114
  dln2-i2c-test --address 0x76 --register 0xD0 --read 1
120
115
 
121
- # BME280
122
- dln2-bme280 --address 0x76 --pretty
123
-
124
116
  # SPI
125
117
  dln2-spi-test
126
118
  dln2-bpw-test
127
119
  ```
128
120
 
129
- ## Examples (as scripts)
121
+ ## Examples
122
+
123
+ ### Core examples (this repo)
124
+
125
+ Basic I/O operations that exercise the wrapper itself:
130
126
 
131
127
  ```bash
132
128
  # SPI JEDEC ID read
133
129
  python3 examples/spidev_test.py
134
130
 
131
+ # SPI bits-per-word cycle
132
+ python3 examples/bpw_tester.py
133
+
135
134
  # GPIO pin toggle (default: Pico LED)
136
135
  python3 examples/gpio_toggle.py --pin 25
137
136
 
137
+ # GPIO pin info
138
+ python3 examples/gpio_info.py --pin 2
139
+
140
+ # GPIO event watch
141
+ python3 examples/gpio_watch.py --pin 2
142
+
138
143
  # I2C bus scan
139
144
  python3 examples/i2c_scan.py
140
145
 
141
- # ADC monitor
146
+ # I2C register read/write
147
+ python3 examples/i2c_test.py --address 0x76 --register 0xD0 --read 1
148
+
149
+ # ADC read
142
150
  python3 examples/adc_info.py
151
+
152
+ # ADC stream
153
+ python3 examples/adc_watch.py --channel 0
154
+ ```
155
+
156
+ ### Device-specific examples (separate repo)
157
+
158
+ Sensor test scripts live in [dln2_examples](https://github.com/syabyr/dln2_examples):
159
+
160
+ ```bash
161
+ git clone https://github.com/syabyr/dln2_examples
162
+ cd dln2_examples/i2c
163
+
164
+ python3 bh1750_full_test.py # BH1750 ambient light @ 0x23
165
+ python3 bme280_full_test.py # BME280 temp/press/humid @ 0x76
166
+ python3 bmp180_full_test.py # BMP180 temp/pressure @ 0x77
167
+ python3 tsl2561_full_test.py # TSL2561 ambient light @ 0x39
168
+ python3 mpu9250_full_test.py # MPU9250 9-axis IMU @ 0x68
143
169
  ```
144
170
 
145
171
  ## Why Unified?
@@ -152,10 +178,31 @@ python3 examples/adc_info.py
152
178
  | Multi-device | Manual USB enumeration | `list_devices()` |
153
179
  | Cross-module data flow | Impossible (4 echo counters) | Trivial (same counter) |
154
180
 
181
+ ## Pin Constraints
182
+
183
+ | Pins | Capability |
184
+ |---|---|
185
+ | 0–22, 26–28 | Full GPIO (input/output + events) |
186
+ | 23, 24, 29 | Output-only (reserved for SMPS / VBUS / VSYS) |
187
+ | 25 | Output-only (on-board LED) |
188
+ | ADC 0–2 | Map to GPIO 26, 27, 28 respectively |
189
+
190
+ ## Firmware Compatibility
191
+
192
+ | dln2 Python | Minimum firmware | Notes |
193
+ |---|---|---|
194
+ | 0.2.1 | [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) `>= v0.2` | SPI bpw validation, ADC pin-sharing fix |
195
+ | 0.2.0 | any | Works with all firmware versions |
196
+
197
+ To flash updated firmware:
198
+ 1. Hold the BOOTSEL button while connecting the Pico (or press and release RESET while holding BOOTSEL)
199
+ 2. Copy `dln2.uf2` to the `RPI-RP2` USB drive that appears
200
+ 3. The Pico reboots automatically with the new firmware
201
+
155
202
  ## Related
156
203
 
157
204
  - [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) — RP2040 firmware
158
- - [st7789 display driver](https://github.com/syabyr/dln2_wrapper) — example ST7789 + BME280 application
205
+ - [dln2_examples](https://github.com/syabyr/dln2_examples) — sensor test scripts (BH1750, BME280, BMP180, TSL2561, MPU9250, …)
159
206
 
160
207
  ## License
161
208
 
@@ -24,4 +24,3 @@ from .gpio import GPIO, \
24
24
  GPIO_EVENT_LEVEL_HIGH, GPIO_EVENT_LEVEL_LOW
25
25
  from .i2c import SMBus, i2c_msg
26
26
  from .adc import ADC
27
- from .bme280 import BME280
@@ -127,18 +127,25 @@ class Dln2Connection:
127
127
  self._setup()
128
128
 
129
129
  def _setup(self):
130
- # Reset USB to clear stale state
131
- try:
132
- self._dev.reset()
133
- except Exception:
134
- pass
130
+ # NOTE: Do NOT call self._dev.reset() here.
131
+ # A USB bus reset only resets the USB peripheral on the RP2040 —
132
+ # SPI PIO/DMA state machines survive it. Commands that hit a
133
+ # half-configured PIO cause a hard fault that kills the firmware
134
+ # and requires a physical replug. Instead, just set configuration.
135
135
 
136
136
  try:
137
137
  self._dev.set_configuration()
138
138
  except Exception:
139
139
  pass
140
140
 
141
- cfg = self._dev.get_active_configuration()
141
+ try:
142
+ cfg = self._dev.get_active_configuration()
143
+ except Exception:
144
+ raise RuntimeError(
145
+ "DLN2 device is not responding — "
146
+ "the Pico firmware may have crashed.\n"
147
+ "Please replug the USB cable to power-cycle the board."
148
+ )
142
149
  intf = cfg[(0, 0)]
143
150
 
144
151
  ep_out = ep_in = None
@@ -409,6 +416,7 @@ class Dln2Connection:
409
416
  return resp
410
417
 
411
418
  def i2c_read(self, addr, length, mem_addr_len=0, mem_addr=0):
419
+ # Use 9-byte header — firmware expects BBBIH for reads
412
420
  payload = struct.pack("<BBBIH", 0, int(addr) & 0x7F,
413
421
  int(mem_addr_len) & 0xFF,
414
422
  int(mem_addr) & 0xFFFFFFFF,
@@ -282,40 +282,6 @@ def i2c_test():
282
282
  sys.exit(1)
283
283
 
284
284
 
285
- # ═══════════════════════════════════════════════════════════════
286
- # BME280
287
- # ═══════════════════════════════════════════════════════════════
288
-
289
- def bme280():
290
- """Read BME280 sensor values (chip ID + measurements)."""
291
- from dln2.bme280 import BME280
292
-
293
- parser = argparse.ArgumentParser(description="Read BME280 values over DLN2 I2C")
294
- parser.add_argument("--address", type=_parse_int, default=0x76, help="BME280 I2C address")
295
- parser.add_argument("--pretty", action="store_true", help="Print pretty JSON")
296
- args = parser.parse_args()
297
-
298
- try:
299
- with BME280(address=args.address) as sensor:
300
- chip_id = sensor.check_chip_id()
301
- sensor.read_calibration()
302
- sensor.configure()
303
- measurement = sensor.read_measurements()
304
-
305
- payload = {
306
- "address": args.address,
307
- "chip_id": chip_id,
308
- **measurement,
309
- }
310
- if args.pretty:
311
- print(json.dumps(payload, indent=2, sort_keys=True))
312
- else:
313
- print(json.dumps(payload, sort_keys=True))
314
- except RuntimeError as e:
315
- print(f"Error: {e}", file=sys.stderr)
316
- sys.exit(1)
317
-
318
-
319
285
  # ═══════════════════════════════════════════════════════════════
320
286
  # SPI
321
287
  # ═══════════════════════════════════════════════════════════════
@@ -339,10 +305,10 @@ def spi_test():
339
305
 
340
306
  try:
341
307
  print("Opening SpiDev (DLN backend)...")
342
- dev.open(0, 0)
343
308
  dev.max_speed_hz = 1_000_000
344
309
  dev.mode = 0
345
310
  dev.bits_per_word = 8
311
+ dev.open(0, 0)
346
312
  tx = [0x9F, 0x00, 0x00, 0x00]
347
313
  print("Sending JEDEC (0x9F) via DLN wrapper... host_hold_cs=", dev.host_hold_cs)
348
314
  rx = dev.xfer2(tx)
@@ -359,23 +325,22 @@ def spi_test():
359
325
 
360
326
 
361
327
  def bpw_test():
362
- """Cycle SPI bits-per-word 4..16 sending a test pattern at each setting."""
328
+ """Cycle SPI bits-per-word 8 and 16 sending a test pattern at each setting."""
363
329
  from dln2.spi import SpiDev
364
330
 
365
- print("Sending one transfer for each BPW 4..16")
331
+ print("Sending one transfer for BPW 8 and 16")
366
332
  try:
367
333
  with SpiDev() as dev:
368
- dev.open(0, 0)
369
334
  dev.max_speed_hz = 1_000_000
370
335
  dev.mode = 0
371
336
 
372
- for bpw in range(4, 17):
337
+ for bpw in (8, 16):
373
338
  dev.bits_per_word = bpw
339
+ dev.open(0, 0)
374
340
 
375
- if bpw <= 8:
376
- pattern = 0xAA & ((1 << bpw) - 1)
377
- else:
378
- pattern = 0xAAAA & ((1 << bpw) - 1)
341
+ mask = (1 << bpw) - 1
342
+ pattern = 0xAA if bpw <= 8 else 0xAAAA
343
+ pattern &= mask
379
344
 
380
345
  try:
381
346
  rx = dev.xfer2([pattern])
@@ -383,7 +348,8 @@ def bpw_test():
383
348
  except Exception as e:
384
349
  print(f"bpw={bpw:2d} tx={pattern:#06x} FAILED: {e}")
385
350
 
386
- time.sleep(0.1)
351
+ dev.close()
352
+ time.sleep(0.2)
387
353
 
388
354
  print("Done.")
389
355
  except RuntimeError as e:
@@ -103,24 +103,28 @@ class SMBus:
103
103
  return self._require().i2c_read(addr, 1)[0]
104
104
 
105
105
  def read_byte_data(self, addr, cmd):
106
- return self._require().i2c_read(addr, 1, mem_addr_len=1,
107
- mem_addr=cmd)[0]
106
+ conn = self._require()
107
+ conn.i2c_write(addr, [cmd])
108
+ return conn.i2c_read(addr, 1)[0]
108
109
 
109
110
  def read_word_data(self, addr, cmd):
110
- rx = self._require().i2c_read(addr, 2, mem_addr_len=1, mem_addr=cmd)
111
+ conn = self._require()
112
+ conn.i2c_write(addr, [cmd])
113
+ rx = conn.i2c_read(addr, 2)
111
114
  return rx[0] | (rx[1] << 8)
112
115
 
113
116
  def read_block_data(self, addr, cmd):
114
- # Read count byte first, then data
115
- count = self._require().i2c_read(addr, 1, mem_addr_len=1,
116
- mem_addr=cmd)[0]
117
+ conn = self._require()
118
+ conn.i2c_write(addr, [cmd])
119
+ count = conn.i2c_read(addr, 1)[0]
117
120
  if count == 0:
118
121
  return []
119
- return list(self._require().i2c_read(addr, count))
122
+ return list(conn.i2c_read(addr, count))
120
123
 
121
124
  def read_i2c_block_data(self, addr, cmd, length=32):
122
- return list(self._require().i2c_read(addr, length,
123
- mem_addr_len=1, mem_addr=cmd))
125
+ conn = self._require()
126
+ conn.i2c_write(addr, [cmd])
127
+ return list(conn.i2c_read(addr, length))
124
128
 
125
129
  # ── Raw access ─────────────────────────────────────
126
130
 
@@ -46,14 +46,33 @@ class SpiDev:
46
46
 
47
47
  @bits_per_word.setter
48
48
  def bits_per_word(self, v):
49
- self._bits_per_word = int(v)
49
+ v = int(v)
50
+ # RP2040 PIO SPI only reliably supports 8 and 16 bit frames.
51
+ # Other values (4-7, 9-15) can trigger firmware DMA/PIO bugs.
52
+ if v not in (8, 16):
53
+ raise ValueError(
54
+ f"bits_per_word must be 8 or 16, got {v}. "
55
+ f"The Pico firmware does not support other frame sizes."
56
+ )
57
+ self._bits_per_word = v
50
58
 
51
59
  def open(self, bus=0, device=0):
52
- """No-op connection already open. For spidev compatibility."""
60
+ """Enable the SPI module. For spidev compatibility."""
61
+ self._conn.spi_enable()
62
+ self._configure()
53
63
  return self
54
64
 
55
65
  def close(self):
56
- pass # connection is shared, caller manages _conn.close()
66
+ """Disable the SPI module so the firmware releases its PIO/DMA state."""
67
+ try:
68
+ self._conn.spi_disable()
69
+ except Exception:
70
+ pass
71
+
72
+ def _configure(self):
73
+ """Sync current settings to the hardware."""
74
+ self._conn.spi_configure(self._mode, self._max_speed_hz,
75
+ self._bits_per_word)
57
76
 
58
77
  def xfer2(self, data):
59
78
  """Full-duplex transfer. Returns MISO data."""
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: dln2
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Unified DLN2 driver — SPI, GPIO, I2C, ADC over a single USB connection. Originally developed for the Pico USB I/O Board.
5
5
  Home-page: https://github.com/syabyr/dln2_wrapper
6
6
  Author: IPM Group
@@ -21,17 +21,6 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: pyusb>=1.2
24
- Dynamic: author
25
- Dynamic: author-email
26
- Dynamic: classifier
27
- Dynamic: description
28
- Dynamic: description-content-type
29
- Dynamic: home-page
30
- Dynamic: license
31
- Dynamic: license-file
32
- Dynamic: requires-dist
33
- Dynamic: requires-python
34
- Dynamic: summary
35
24
 
36
25
  # dln2 — Unified DLN2 USB I/O Board Wrapper
37
26
 
@@ -118,10 +107,6 @@ smbus-compatible API: `write_byte()`, `read_byte_data()`,
118
107
  `read_channel(channel)`, `read_all()`, `read_volts(channel)`,
119
108
  `set_resolution(bits)`, `enable_channel(ch)`, `disable_channel(ch)`
120
109
 
121
- ### BME280
122
- `read_chip_id()`, `get_temperature()`, `get_humidity()`, `get_pressure()`,
123
- `get_altitude()`, `get_all()`
124
-
125
110
  ## CLI Commands
126
111
 
127
112
  After installation, the following commands are available:
@@ -135,7 +120,6 @@ After installation, the following commands are available:
135
120
  | `dln2-adc-watch` | Stream one ADC channel |
136
121
  | `dln2-i2c-scan` | Scan I2C bus for devices |
137
122
  | `dln2-i2c-test` | Read/write I2C register |
138
- | `dln2-bme280` | Read BME280 sensor measurements |
139
123
  | `dln2-spi-test` | Send SPI JEDEC ID probe |
140
124
  | `dln2-bpw-test` | Cycle SPI bits-per-word 4..16 |
141
125
 
@@ -153,28 +137,59 @@ dln2-adc-watch --channel 0 --interval 0.5
153
137
  dln2-i2c-scan
154
138
  dln2-i2c-test --address 0x76 --register 0xD0 --read 1
155
139
 
156
- # BME280
157
- dln2-bme280 --address 0x76 --pretty
158
-
159
140
  # SPI
160
141
  dln2-spi-test
161
142
  dln2-bpw-test
162
143
  ```
163
144
 
164
- ## Examples (as scripts)
145
+ ## Examples
146
+
147
+ ### Core examples (this repo)
148
+
149
+ Basic I/O operations that exercise the wrapper itself:
165
150
 
166
151
  ```bash
167
152
  # SPI JEDEC ID read
168
153
  python3 examples/spidev_test.py
169
154
 
155
+ # SPI bits-per-word cycle
156
+ python3 examples/bpw_tester.py
157
+
170
158
  # GPIO pin toggle (default: Pico LED)
171
159
  python3 examples/gpio_toggle.py --pin 25
172
160
 
161
+ # GPIO pin info
162
+ python3 examples/gpio_info.py --pin 2
163
+
164
+ # GPIO event watch
165
+ python3 examples/gpio_watch.py --pin 2
166
+
173
167
  # I2C bus scan
174
168
  python3 examples/i2c_scan.py
175
169
 
176
- # ADC monitor
170
+ # I2C register read/write
171
+ python3 examples/i2c_test.py --address 0x76 --register 0xD0 --read 1
172
+
173
+ # ADC read
177
174
  python3 examples/adc_info.py
175
+
176
+ # ADC stream
177
+ python3 examples/adc_watch.py --channel 0
178
+ ```
179
+
180
+ ### Device-specific examples (separate repo)
181
+
182
+ Sensor test scripts live in [dln2_examples](https://github.com/syabyr/dln2_examples):
183
+
184
+ ```bash
185
+ git clone https://github.com/syabyr/dln2_examples
186
+ cd dln2_examples/i2c
187
+
188
+ python3 bh1750_full_test.py # BH1750 ambient light @ 0x23
189
+ python3 bme280_full_test.py # BME280 temp/press/humid @ 0x76
190
+ python3 bmp180_full_test.py # BMP180 temp/pressure @ 0x77
191
+ python3 tsl2561_full_test.py # TSL2561 ambient light @ 0x39
192
+ python3 mpu9250_full_test.py # MPU9250 9-axis IMU @ 0x68
178
193
  ```
179
194
 
180
195
  ## Why Unified?
@@ -187,10 +202,31 @@ python3 examples/adc_info.py
187
202
  | Multi-device | Manual USB enumeration | `list_devices()` |
188
203
  | Cross-module data flow | Impossible (4 echo counters) | Trivial (same counter) |
189
204
 
205
+ ## Pin Constraints
206
+
207
+ | Pins | Capability |
208
+ |---|---|
209
+ | 0–22, 26–28 | Full GPIO (input/output + events) |
210
+ | 23, 24, 29 | Output-only (reserved for SMPS / VBUS / VSYS) |
211
+ | 25 | Output-only (on-board LED) |
212
+ | ADC 0–2 | Map to GPIO 26, 27, 28 respectively |
213
+
214
+ ## Firmware Compatibility
215
+
216
+ | dln2 Python | Minimum firmware | Notes |
217
+ |---|---|---|
218
+ | 0.2.1 | [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) `>= v0.2` | SPI bpw validation, ADC pin-sharing fix |
219
+ | 0.2.0 | any | Works with all firmware versions |
220
+
221
+ To flash updated firmware:
222
+ 1. Hold the BOOTSEL button while connecting the Pico (or press and release RESET while holding BOOTSEL)
223
+ 2. Copy `dln2.uf2` to the `RPI-RP2` USB drive that appears
224
+ 3. The Pico reboots automatically with the new firmware
225
+
190
226
  ## Related
191
227
 
192
228
  - [pico-usb-io-board](https://github.com/syabyr/pico-usb-io-board) — RP2040 firmware
193
- - [st7789 display driver](https://github.com/syabyr/dln2_wrapper) — example ST7789 + BME280 application
229
+ - [dln2_examples](https://github.com/syabyr/dln2_examples) — sensor test scripts (BH1750, BME280, BMP180, TSL2561, MPU9250, …)
194
230
 
195
231
  ## License
196
232
 
@@ -1,7 +1,6 @@
1
1
  [console_scripts]
2
2
  dln2-adc-info = dln2.cli:adc_info
3
3
  dln2-adc-watch = dln2.cli:adc_watch
4
- dln2-bme280 = dln2.cli:bme280
5
4
  dln2-bpw-test = dln2.cli:bpw_test
6
5
  dln2-gpio-info = dln2.cli:gpio_info
7
6
  dln2-gpio-toggle = dln2.cli:gpio_toggle
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
5
5
 
6
6
  setup(
7
7
  name="dln2",
8
- version="0.2.0",
8
+ version="0.2.2",
9
9
  description="Unified DLN2 driver — SPI, GPIO, I2C, ADC over a single USB connection. "
10
10
  "Originally developed for the Pico USB I/O Board.",
11
11
  long_description=open("README.md").read(),
@@ -14,7 +14,7 @@ setup(
14
14
  author="IPM Group",
15
15
  author_email="contact@ipmgroup.dev",
16
16
  license="MIT",
17
- packages=find_packages(exclude=["examples"]),
17
+ packages=find_packages(exclude=["examples", "i2c", "i2c.*"]),
18
18
  package_data={"dln2": ["py.typed"]},
19
19
  install_requires=[
20
20
  "pyusb>=1.2",
@@ -29,7 +29,6 @@ setup(
29
29
  "dln2-adc-watch=dln2.cli:adc_watch",
30
30
  "dln2-i2c-scan=dln2.cli:i2c_scan",
31
31
  "dln2-i2c-test=dln2.cli:i2c_test",
32
- "dln2-bme280=dln2.cli:bme280",
33
32
  "dln2-spi-test=dln2.cli:spi_test",
34
33
  "dln2-bpw-test=dln2.cli:bpw_test",
35
34
  ],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes