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

communication.mddocs/

0

# Communication Protocols

1

2

Hardware and software implementations of I2C, SPI, and UART communication protocols with automatic resource locking and CircuitPython compatibility. Provides unified interfaces across diverse hardware platforms with automatic platform detection and driver loading.

3

4

## Capabilities

5

6

### I2C Communication

7

8

The I2C class provides Inter-Integrated Circuit (I2C) communication with automatic platform detection and resource management. Supports both hardware and software implementations depending on the platform.

9

10

```python { .api }

11

class I2C(Lockable):

12

def __init__(self, scl, sda, frequency: int = 100000):

13

"""

14

Initialize I2C bus.

15

16

Args:

17

scl: SCL (clock) pin object from board module

18

sda: SDA (data) pin object from board module

19

frequency: Bus frequency in Hz (default 100000, ignored on Linux)

20

21

Raises:

22

ValueError: If no hardware I2C available on specified pins

23

"""

24

25

def deinit(self) -> None:

26

"""Release I2C bus resources"""

27

28

def scan(self) -> list[int]:

29

"""

30

Scan for I2C devices on the bus.

31

32

Returns:

33

list[int]: List of 7-bit device addresses found on the bus

34

"""

35

36

def readfrom_into(self, address: int, buffer: bytearray, *, start: int = 0, end: int = None) -> None:

37

"""

38

Read from I2C device into existing buffer.

39

40

Args:

41

address: 7-bit I2C device address

42

buffer: Buffer to read data into

43

start: Starting index in buffer (default 0)

44

end: Ending index in buffer (default None for full buffer)

45

"""

46

47

def writeto(self, address: int, buffer: bytes, *, start: int = 0, end: int = None) -> int:

48

"""

49

Write data to I2C device.

50

51

Args:

52

address: 7-bit I2C device address

53

buffer: Data to write (bytes or str)

54

start: Starting index in buffer (default 0)

55

end: Ending index in buffer (default None for full buffer)

56

57

Returns:

58

int: Number of bytes written

59

"""

60

61

def writeto_then_readfrom(

62

self,

63

address: int,

64

buffer_out: bytes,

65

buffer_in: bytearray,

66

*,

67

out_start: int = 0,

68

out_end: int = None,

69

in_start: int = 0,

70

in_end: int = None,

71

stop: bool = False

72

) -> None:

73

"""

74

Write to device then read from device (common for register reads).

75

76

Args:

77

address: 7-bit I2C device address

78

buffer_out: Data to write first

79

buffer_in: Buffer to read response into

80

out_start: Start index for output buffer

81

out_end: End index for output buffer

82

in_start: Start index for input buffer

83

in_end: End index for input buffer

84

stop: Send stop condition between write and read

85

"""

86

```

87

88

### SPI Communication

89

90

The SPI class provides Serial Peripheral Interface (SPI) communication with configurable parameters and automatic platform detection. Includes resource locking for thread safety.

91

92

```python { .api }

93

class SPI(Lockable):

94

def __init__(self, clock, MOSI=None, MISO=None):

95

"""

96

Initialize SPI bus.

97

98

Args:

99

clock: SCK (clock) pin object from board module (required)

100

MOSI: MOSI (Master Out Slave In) pin object (optional for read-only)

101

MISO: MISO (Master In Slave Out) pin object (optional for write-only)

102

103

Raises:

104

ValueError: If no hardware SPI available on specified pins

105

"""

106

107

def configure(self, baudrate: int = 100000, polarity: int = 0, phase: int = 0, bits: int = 8) -> None:

108

"""

109

Configure SPI parameters. Must be called after try_lock().

110

111

Args:

112

baudrate: Clock frequency in Hz (default 100000)

113

polarity: Clock polarity - 0 (idle low) or 1 (idle high)

114

phase: Clock phase - 0 (sample on leading edge) or 1 (trailing edge)

115

bits: Data word size in bits (default 8)

116

117

Raises:

118

RuntimeError: If SPI bus is not locked

119

"""

120

121

def deinit(self) -> None:

122

"""Release SPI bus resources"""

123

124

@property

125

def frequency(self) -> int:

126

"""

127

Current SPI frequency in Hz.

128

129

Returns:

130

int: Current baudrate

131

132

Raises:

133

NotImplementedError: If not supported on platform

134

"""

135

136

def write(self, buf: bytes, start: int = 0, end: int = None) -> None:

137

"""

138

Write data to SPI bus.

139

140

Args:

141

buf: Data to write

142

start: Starting index in buffer (default 0)

143

end: Ending index in buffer (default None for full buffer)

144

"""

145

146

def readinto(self, buf: bytearray, start: int = 0, end: int = None, write_value: int = 0) -> None:

147

"""

148

Read data from SPI bus into buffer.

149

150

Args:

151

buf: Buffer to read data into

152

start: Starting index in buffer (default 0)

153

end: Ending index in buffer (default None for full buffer)

154

write_value: Value to write during read (default 0)

155

"""

156

157

def write_readinto(

158

self,

159

buffer_out: bytes,

160

buffer_in: bytearray,

161

out_start: int = 0,

162

out_end: int = None,

163

in_start: int = 0,

164

in_end: int = None

165

) -> None:

166

"""

167

Simultaneously write and read data (full-duplex).

168

169

Args:

170

buffer_out: Data to write

171

buffer_in: Buffer to read response into

172

out_start: Start index for output buffer

173

out_end: End index for output buffer

174

in_start: Start index for input buffer

175

in_end: End index for input buffer

176

"""

177

```

178

179

### UART Communication

180

181

The UART class provides Universal Asynchronous Receiver-Transmitter (UART) serial communication. Not supported on Linux platforms - use pyserial instead.

182

183

```python { .api }

184

class UART(Lockable):

185

class Parity:

186

ODD: Parity # Odd parity checking

187

EVEN: Parity # Even parity checking

188

189

def __init__(

190

self,

191

tx,

192

rx,

193

baudrate: int = 9600,

194

bits: int = 8,

195

parity = None,

196

stop: int = 1,

197

timeout: int = 1000,

198

receiver_buffer_size: int = 64,

199

flow = None

200

):

201

"""

202

Initialize UART interface.

203

204

Args:

205

tx: TX (transmit) pin object from board module

206

rx: RX (receive) pin object from board module

207

baudrate: Communication speed in baud (default 9600)

208

bits: Data bits per character (default 8)

209

parity: Parity checking (None, UART.Parity.ODD, UART.Parity.EVEN)

210

stop: Stop bits (default 1)

211

timeout: Read timeout in milliseconds (default 1000)

212

receiver_buffer_size: RX buffer size in bytes (default 64)

213

flow: Flow control (not implemented, raises NotImplementedError)

214

215

Raises:

216

RuntimeError: On Linux platforms (use pyserial instead)

217

ValueError: If no hardware UART available on specified pins

218

NotImplementedError: If flow control is specified

219

"""

220

221

def deinit(self) -> None:

222

"""Release UART resources"""

223

224

def read(self, nbytes: int = None) -> bytes:

225

"""

226

Read data from UART.

227

228

Args:

229

nbytes: Number of bytes to read (None for all available)

230

231

Returns:

232

bytes: Data read from UART

233

"""

234

235

def readinto(self, buf: bytearray, nbytes: int = None) -> int:

236

"""

237

Read data from UART into buffer.

238

239

Args:

240

buf: Buffer to read data into

241

nbytes: Number of bytes to read (None for buffer size)

242

243

Returns:

244

int: Number of bytes actually read

245

"""

246

247

def readline(self) -> bytes:

248

"""

249

Read a line of characters up to newline.

250

251

Returns:

252

bytes: Line data including newline character

253

"""

254

255

def write(self, buf: bytes) -> int:

256

"""

257

Write data to UART.

258

259

Args:

260

buf: Data to write

261

262

Returns:

263

int: Number of bytes written

264

"""

265

```

266

267

## Usage Examples

268

269

### I2C Device Communication

270

271

```python

272

import board

273

import busio

274

import time

275

276

# Initialize I2C bus

277

i2c = busio.I2C(board.SCL, board.SDA)

278

279

# Scan for devices

280

with i2c:

281

devices = i2c.scan()

282

print(f"I2C devices found: {[hex(d) for d in devices]}")

283

284

# Communicate with device at address 0x48

285

device_addr = 0x48

286

if device_addr in devices:

287

with i2c:

288

# Write register address

289

i2c.writeto(device_addr, bytes([0x00]))

290

291

# Read response

292

buffer = bytearray(2)

293

i2c.readfrom_into(device_addr, buffer)

294

print(f"Device response: {buffer}")

295

296

# Combined write-then-read operation

297

with i2c:

298

result = bytearray(2)

299

i2c.writeto_then_readfrom(device_addr, bytes([0x01]), result)

300

print(f"Register 0x01 value: {result}")

301

302

# Cleanup

303

i2c.deinit()

304

```

305

306

### SPI Device Communication

307

308

```python

309

import board

310

import busio

311

import digitalio

312

313

# Initialize SPI bus

314

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

315

316

# Setup chip select pin

317

cs = digitalio.DigitalInOut(board.D5)

318

cs.direction = digitalio.Direction.OUTPUT

319

cs.value = True

320

321

# Configure and use SPI

322

while not spi.try_lock():

323

pass

324

325

try:

326

spi.configure(baudrate=1000000, polarity=0, phase=0)

327

328

# Write data

329

cs.value = False

330

spi.write(bytes([0x01, 0x02, 0x03]))

331

cs.value = True

332

333

time.sleep(0.001)

334

335

# Read data

336

cs.value = False

337

result = bytearray(3)

338

spi.readinto(result)

339

cs.value = True

340

341

print(f"SPI response: {result}")

342

343

finally:

344

spi.unlock()

345

346

# Cleanup

347

spi.deinit()

348

cs.deinit()

349

```

350

351

### UART Serial Communication

352

353

```python

354

# Note: UART not supported on Linux - use pyserial library instead

355

import board

356

import busio

357

358

try:

359

# Initialize UART (MicroPython/embedded platforms only)

360

uart = busio.UART(board.TX, board.RX, baudrate=115200)

361

362

# Send data

363

uart.write(b"Hello, UART!\n")

364

365

# Read response

366

data = uart.read(10)

367

if data:

368

print(f"Received: {data}")

369

370

# Read line

371

line = uart.readline()

372

if line:

373

print(f"Line: {line}")

374

375

# Cleanup

376

uart.deinit()

377

378

except RuntimeError as e:

379

print(f"UART not supported: {e}")

380

print("Use pyserial library for serial communication on Linux")

381

```

382

383

### Resource Management

384

385

```python

386

import board

387

import busio

388

389

# Using context managers for automatic cleanup

390

with busio.I2C(board.SCL, board.SDA) as i2c:

391

devices = i2c.scan()

392

print(f"Found devices: {devices}")

393

394

# Manual locking for SPI

395

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

396

397

# Lock before configuration

398

if spi.try_lock():

399

try:

400

spi.configure(baudrate=500000)

401

spi.write(b"test data")

402

finally:

403

spi.unlock()

404

else:

405

print("Could not acquire SPI lock")

406

407

spi.deinit()

408

```

409

410

## Platform Considerations

411

412

### I2C Support

413

- **Hardware I2C**: Most embedded Linux systems, RP2040, specialized adapters

414

- **Software I2C**: Available via bitbangio for platforms without hardware support

415

- **Frequency**: Linux ignores frequency parameter, uses system defaults

416

417

### SPI Support

418

- **Full-duplex**: Most platforms support simultaneous read/write operations

419

- **Pin flexibility**: MOSI/MISO pins optional for unidirectional communication

420

- **Locking required**: Must call try_lock() before configure() and operations

421

422

### UART Limitations

423

- **Linux**: Not supported - use pyserial library instead

424

- **MicroPython**: Full support with hardware flow control limitations

425

- **Embedded platforms**: Variable feature support depending on hardware

426

427

### Error Handling

428

429

```python

430

import board

431

import busio

432

433

try:

434

i2c = busio.I2C(board.SCL, board.SDA)

435

# Use I2C...

436

i2c.deinit()

437

except ValueError as e:

438

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

439

except RuntimeError as e:

440

print(f"Platform limitation: {e}")

441

```