or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analog-io.mdbitbangio.mdboard-pins.mdcommunication.mdcore-framework.mddigital-io.mdindex.mdperipherals.mdpwm-pulse.mdutilities.md

core-framework.mddocs/

0

# Core Framework

1

2

Base classes and utilities that provide resource management, platform detection, and CircuitPython compatibility features. Forms the foundation for all hardware interface classes with automatic cleanup, resource locking, and cross-platform abstractions.

3

4

## Capabilities

5

6

### Context Management Base Classes

7

8

Fundamental base classes that provide automatic resource cleanup and exclusive resource access patterns used throughout the Blinka ecosystem.

9

10

```python { .api }

11

class ContextManaged:

12

def __enter__(self):

13

"""Context manager entry - returns self"""

14

15

def __exit__(self, exc_type, exc_val, exc_tb):

16

"""Context manager exit - automatically calls deinit()"""

17

18

def deinit(self) -> None:

19

"""

20

Free any hardware resources used by the object.

21

22

Note:

23

Override this method in subclasses to implement

24

specific resource cleanup logic.

25

"""

26

27

class Lockable(ContextManaged):

28

def try_lock(self) -> bool:

29

"""

30

Attempt to acquire exclusive lock on resource.

31

32

Returns:

33

bool: True if lock acquired, False if already locked

34

35

Note:

36

Must call try_lock() before using shared resources like SPI/I2C.

37

Always pair with unlock() or use context manager for safety.

38

"""

39

40

def unlock(self) -> None:

41

"""

42

Release exclusive lock on resource.

43

44

Note:

45

Only call if try_lock() returned True.

46

Context manager automatically handles unlock.

47

"""

48

```

49

50

### CircuitPython Compatibility

51

52

Enum class that provides CircuitPython-style static symbols and introspection capabilities for constants and configuration values.

53

54

```python { .api }

55

class Enum:

56

def __repr__(self) -> str:

57

"""

58

Return dot-subscripted path to enum instance.

59

60

Returns:

61

str: Module path to enum value (e.g., "digitalio.Direction.OUTPUT")

62

"""

63

64

@classmethod

65

def iteritems(cls):

66

"""

67

Iterate over class attributes that are instances of this enum.

68

69

Yields:

70

tuple: (key, value) pairs for enum constants

71

72

Example:

73

for name, value in Direction.iteritems():

74

print(f"{name}: {value}")

75

"""

76

```

77

78

### Configuration Management

79

80

Utilities for loading configuration settings and applying platform-specific patches to ensure compatibility across diverse environments.

81

82

```python { .api }

83

def load_settings_toml() -> dict:

84

"""

85

Load values from settings.toml into environment variables.

86

87

Returns:

88

dict: Parsed TOML settings

89

90

Raises:

91

FileNotFoundError: If settings.toml not found in current directory

92

TOMLDecodeError: If settings.toml has invalid syntax

93

ValueError: If settings contain unsupported data types

94

95

Note:

96

Only supports bool, int, float, and str values.

97

Settings are added to os.environ for later access via os.getenv().

98

Existing environment variables are not overwritten.

99

"""

100

101

def patch_system() -> None:

102

"""

103

Apply platform-specific patches to system modules.

104

105

Note:

106

Patches the time module to ensure consistent behavior

107

across CPython, MicroPython, and CircuitPython platforms.

108

Called automatically during Blinka initialization.

109

"""

110

```

111

112

### Hardware Pin Abstraction

113

114

Pin class that provides a common interface for hardware pin references across all supported platforms and microcontrollers.

115

116

```python { .api }

117

class Pin:

118

"""

119

Hardware pin reference object.

120

121

Note:

122

Pin objects are typically accessed through board module constants

123

(e.g., board.D18) rather than created directly.

124

Provides platform-agnostic pin identification.

125

"""

126

id: int # Platform-specific pin identifier

127

```

128

129

### Platform Detection

130

131

Runtime platform detection system that identifies hardware capabilities and loads appropriate drivers automatically.

132

133

```python { .api }

134

# Platform detection globals (read-only)

135

detector: object # Platform detection instance from adafruit-platformdetect

136

chip_id: str # Detected microcontroller/SoC identifier

137

board_id: str # Detected development board identifier

138

implementation: str # Python implementation name ("cpython", "micropython", etc.)

139

140

# Platform-appropriate sleep function

141

sleep: function # Time.sleep or utime.sleep depending on platform

142

```

143

144

### Precision Timing

145

146

Microsecond-precision delay function for applications requiring accurate timing control.

147

148

```python { .api }

149

def delay_us(delay: int) -> None:

150

"""

151

Sleep for the specified number of microseconds.

152

153

Args:

154

delay: Delay duration in microseconds

155

156

Note:

157

Provides microsecond precision timing for bit-banging protocols

158

and other time-critical operations. Actual precision depends on

159

platform capabilities and system load.

160

"""

161

```

162

163

## Usage Examples

164

165

### Context Manager Pattern

166

167

```python

168

import digitalio

169

import board

170

import time

171

172

# Using context manager for automatic cleanup

173

with digitalio.DigitalInOut(board.D18) as led:

174

led.direction = digitalio.Direction.OUTPUT

175

176

# LED will be automatically cleaned up when exiting context

177

for i in range(10):

178

led.value = True

179

time.sleep(0.5)

180

led.value = False

181

time.sleep(0.5)

182

183

# LED pin is automatically released here

184

print("LED cleanup completed automatically")

185

```

186

187

### Manual Resource Management

188

189

```python

190

import digitalio

191

import board

192

193

# Manual resource management

194

led = digitalio.DigitalInOut(board.D18)

195

led.direction = digitalio.Direction.OUTPUT

196

197

try:

198

# Use the LED

199

led.value = True

200

# ... do work ...

201

202

finally:

203

# Always clean up resources

204

led.deinit()

205

```

206

207

### Resource Locking for Shared Buses

208

209

```python

210

import busio

211

import board

212

import time

213

214

# SPI bus requires locking for thread safety

215

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

216

217

# Method 1: Manual locking

218

if spi.try_lock():

219

try:

220

spi.configure(baudrate=1000000)

221

spi.write(b'\x01\x02\x03')

222

finally:

223

spi.unlock()

224

else:

225

print("Could not acquire SPI lock")

226

227

# Method 2: Context manager (automatic locking)

228

with spi:

229

spi.configure(baudrate=500000)

230

spi.write(b'\x04\x05\x06')

231

232

spi.deinit()

233

```

234

235

### Custom Enum Definition

236

237

```python

238

from adafruit_blinka import Enum

239

240

class MyConstants(Enum):

241

"""Custom enum for application constants"""

242

pass

243

244

# Define enum values

245

MyConstants.OPTION_A = MyConstants()

246

MyConstants.OPTION_B = MyConstants()

247

MyConstants.OPTION_C = MyConstants()

248

249

# Use enum values

250

current_mode = MyConstants.OPTION_A

251

print(f"Current mode: {current_mode}") # Shows full path

252

253

# Iterate over enum values

254

for name, value in MyConstants.iteritems():

255

print(f"Available option: {name}")

256

```

257

258

### Settings Configuration

259

260

```python

261

# settings.toml file:

262

# wifi_ssid = "MyNetwork"

263

# wifi_password = "secret123"

264

# debug_enabled = true

265

# max_retries = 5

266

# timeout = 30.5

267

268

from adafruit_blinka import load_settings_toml

269

import os

270

271

try:

272

settings = load_settings_toml()

273

print("Loaded settings:", settings)

274

275

# Access via environment variables

276

wifi_ssid = os.getenv("wifi_ssid", "default_network")

277

debug_mode = os.getenv("debug_enabled", "false").lower() == "true"

278

max_retries = int(os.getenv("max_retries", "3"))

279

280

print(f"WiFi SSID: {wifi_ssid}")

281

print(f"Debug mode: {debug_mode}")

282

print(f"Max retries: {max_retries}")

283

284

except FileNotFoundError:

285

print("No settings.toml found, using defaults")

286

except ValueError as e:

287

print(f"Invalid settings format: {e}")

288

```

289

290

### Platform Detection

291

292

```python

293

from adafruit_blinka.agnostic import detector, chip_id, board_id, implementation

294

295

# Check what platform we're running on

296

print(f"Board: {board_id}")

297

print(f"Chip: {chip_id}")

298

print(f"Python implementation: {implementation}")

299

300

# Platform-specific feature detection

301

if detector.board.any_raspberry_pi:

302

print("Running on Raspberry Pi")

303

if detector.board.any_raspberry_pi_5_board:

304

print("Pi 5 detected - using lgpio")

305

else:

306

print("Pi 4 or earlier - using RPi.GPIO")

307

308

elif detector.board.any_jetson_board:

309

print("Running on NVIDIA Jetson")

310

311

elif detector.board.ftdi_ft232h:

312

print("Using FTDI FT232H USB adapter")

313

314

# Check for specific chip families

315

if chip_id.startswith("BCM"):

316

print("Broadcom chip detected")

317

elif chip_id.startswith("RK"):

318

print("Rockchip SoC detected")

319

elif chip_id == "RP2040":

320

print("Raspberry Pi Pico RP2040 detected")

321

322

# Adapt behavior based on platform

323

if detector.board.any_embedded_linux:

324

print("Full Linux system - all features available")

325

else:

326

print("Microcontroller or adapter - limited features")

327

```

328

329

### Precision Timing

330

331

```python

332

import microcontroller

333

import board

334

import digitalio

335

336

# Bit-banging a custom protocol with precise timing

337

data_pin = digitalio.DigitalInOut(board.D18)

338

data_pin.direction = digitalio.Direction.OUTPUT

339

340

def send_bit(bit_value):

341

"""Send a single bit with precise timing"""

342

if bit_value:

343

# Send '1' bit: 800ns high, 450ns low

344

data_pin.value = True

345

microcontroller.delay_us(1) # ~800ns (rounded to 1μs)

346

data_pin.value = False

347

microcontroller.delay_us(1) # ~450ns (rounded to 1μs)

348

else:

349

# Send '0' bit: 400ns high, 850ns low

350

data_pin.value = True

351

microcontroller.delay_us(1) # ~400ns (rounded to 1μs)

352

data_pin.value = False

353

microcontroller.delay_us(1) # ~850ns (rounded to 1μs)

354

355

def send_byte(byte_value):

356

"""Send 8 bits with precise timing"""

357

for bit in range(8):

358

bit_value = (byte_value >> (7 - bit)) & 1

359

send_bit(bit_value)

360

361

# Send custom protocol data

362

try:

363

for data in [0xFF, 0x00, 0xAA, 0x55]:

364

send_byte(data)

365

microcontroller.delay_us(10) # Inter-byte delay

366

367

finally:

368

data_pin.deinit()

369

```

370

371

### Custom Hardware Class

372

373

```python

374

from adafruit_blinka import ContextManaged

375

import board

376

import digitalio

377

import time

378

379

class CustomSensor(ContextManaged):

380

"""Example custom hardware class using Blinka patterns"""

381

382

def __init__(self, data_pin, clock_pin):

383

self._data = digitalio.DigitalInOut(data_pin)

384

self._data.direction = digitalio.Direction.INPUT

385

386

self._clock = digitalio.DigitalInOut(clock_pin)

387

self._clock.direction = digitalio.Direction.OUTPUT

388

self._clock.value = False

389

390

def read_value(self):

391

"""Read sensor value using custom protocol"""

392

# Generate clock pulses and read data

393

value = 0

394

for bit in range(8):

395

self._clock.value = True

396

time.sleep(0.001) # 1ms clock high

397

398

if self._data.value:

399

value |= (1 << (7 - bit))

400

401

self._clock.value = False

402

time.sleep(0.001) # 1ms clock low

403

404

return value

405

406

def deinit(self):

407

"""Clean up hardware resources"""

408

if hasattr(self, '_data'):

409

self._data.deinit()

410

if hasattr(self, '_clock'):

411

self._clock.deinit()

412

413

# Use custom sensor class

414

with CustomSensor(board.D2, board.D3) as sensor:

415

for _ in range(10):

416

value = sensor.read_value()

417

print(f"Sensor reading: {value}")

418

time.sleep(1)

419

420

# Automatic cleanup when exiting context

421

```

422

423

## Platform Considerations

424

425

### Context Manager Benefits

426

427

- **Automatic Cleanup**: Resources always released, even on exceptions

428

- **Thread Safety**: Proper cleanup in multi-threaded applications

429

- **Memory Management**: Prevents resource leaks in long-running programs

430

- **Best Practice**: Follows Python conventions and CircuitPython patterns

431

432

### Resource Locking

433

434

**Required For:**

435

- SPI buses (shared among multiple devices)

436

- I2C buses (shared among multiple devices)

437

- Any hardware resource accessed by multiple threads

438

439

**Not Required For:**

440

- GPIO pins (exclusive to single DigitalInOut instance)

441

- PWM outputs (exclusive to single PWMOut instance)

442

- UART ports (typically exclusive to single UART instance)

443

444

### Platform Detection Reliability

445

446

**Automatic Detection Works For:**

447

- Standard development boards with known signatures

448

- Well-established SoC families with device tree information

449

- USB adapters with unique vendor/product IDs

450

451

**Manual Configuration May Be Needed For:**

452

- Custom boards without standard signatures

453

- Generic x86 systems without GPIO hardware

454

- Embedded systems with non-standard configurations

455

456

### Error Handling

457

458

```python

459

from adafruit_blinka import ContextManaged, load_settings_toml

460

import board

461

462

# Handle platform compatibility issues

463

try:

464

import pwmio

465

pwm = pwmio.PWMOut(board.D18)

466

print("PWM available")

467

pwm.deinit()

468

except (RuntimeError, AttributeError) as e:

469

print(f"PWM not available: {e}")

470

471

# Handle configuration loading errors

472

try:

473

settings = load_settings_toml()

474

except FileNotFoundError:

475

print("Using default configuration")

476

settings = {}

477

except ValueError as e:

478

print(f"Configuration error: {e}")

479

settings = {}

480

481

# Handle resource contention

482

import busio

483

484

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

485

486

# Timeout pattern for resource acquisition

487

import time

488

timeout = 5.0 # seconds

489

start_time = time.time()

490

491

while not spi.try_lock():

492

if time.time() - start_time > timeout:

493

print("Could not acquire SPI lock within timeout")

494

break

495

time.sleep(0.01)

496

else:

497

try:

498

# Use SPI bus

499

spi.configure(baudrate=1000000)

500

spi.write(b'\x01\x02\x03')

501

finally:

502

spi.unlock()

503

504

spi.deinit()

505

```