6# Badger 2350 Hardware Integration
7
8Interface with GPIO pins, sensors, and external hardware on the Badger 2350 badge using I2C, SPI, and digital I/O.
9
10## GPIO Basics
11
12### Pin Configuration
13
14```python
15from machine import Pin
16
17# Configure pin as output (LED, relay, etc.)
18led = Pin(25, Pin.OUT)
19led.value(1) # Turn on (HIGH)
20led.value(0) # Turn off (LOW)
21led.toggle() # Toggle state
22
23# Configure pin as input (button, switch, etc.)
24button = Pin(15, Pin.IN, Pin.PULL_UP)
25if button.value() == 0: # Button pressed (pulled to ground)
26 print("Button pressed!")
27
28# Configure pin as input with pull-down
29sensor = Pin(16, Pin.IN, Pin.PULL_DOWN)
30```
31
32### PWM (Pulse Width Modulation)
33
34```python
35from machine import Pin, PWM
36
37# Control LED brightness or servo motor
38pwm = PWM(Pin(25))
39pwm.freq(1000) # Set frequency to 1kHz
40
41# Set duty cycle (0-65535, where 65535 is 100%)
42pwm.duty_u16(32768) # 50% brightness
43pwm.duty_u16(16384) # 25% brightness
44pwm.duty_u16(65535) # 100% brightness
45
46# Cleanup
47pwm.deinit()
48```
49
50### Interrupts
51
52```python
53from machine import Pin
54
55button = Pin(15, Pin.IN, Pin.PULL_UP)
56
57def button_callback(pin):
58 print(f"Button pressed! Pin: {pin}")
59
60# Trigger on falling edge (button press)
61button.irq(trigger=Pin.IRQ_FALLING, handler=button_callback)
62
63# Trigger on rising edge (button release)
64button.irq(trigger=Pin.IRQ_RISING, handler=button_callback)
65
66# Trigger on both edges
67button.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=button_callback)
68```
69
70## I2C Communication
71
72### I2C Setup
73
74```python
75from machine import I2C, Pin
76
77# Initialize I2C (QWIIC connector uses specific pins)
78i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
79
80# Scan for connected devices
81devices = i2c.scan()
82print(f"Found {len(devices)} I2C devices:")
83for device in devices:
84 print(f" Address: 0x{device:02x}")
85```
86
87### Reading from I2C Device
88
89```python
90# Read data from I2C device
91address = 0x48 # Example: Temperature sensor
92data = i2c.readfrom(address, 2) # Read 2 bytes
93print(f"Raw data: {data}")
94
95# Read from specific register
96register = 0x00
97i2c.writeto(address, bytes([register])) # Select register
98data = i2c.readfrom(address, 2) # Read data
99```
100
101### Writing to I2C Device
102
103```python
104# Write single byte
105address = 0x48
106data = bytes([0x01, 0xA0])
107i2c.writeto(address, data)
108
109# Write to specific register
110register = 0x01
111value = 0xFF
112i2c.writeto(address, bytes([register, value]))
113```
114
115## Common I2C Sensors
116
117### BME280 (Temperature, Humidity, Pressure)
118
119```python
120from machine import I2C, Pin
121import time
122
123class BME280:
124 def __init__(self, i2c, address=0x76):
125 self.i2c = i2c
126 self.address = address
127
128 def read_temp(self):
129 # Read temperature register
130 data = self.i2c.readfrom_mem(self.address, 0xFA, 3)
131 temp_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
132 # Apply calibration (simplified)
133 temp_c = temp_raw / 100.0
134 return temp_c
135
136 def read_humidity(self):
137 # Read humidity register
138 data = self.i2c.readfrom_mem(self.address, 0xFD, 2)
139 hum_raw = (data[0] << 8) | data[1]
140 humidity = hum_raw / 1024.0
141 return humidity
142
143# Usage
144i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
145sensor = BME280(i2c)
146
147temp = sensor.read_temp()
148humidity = sensor.read_humidity()
149print(f"Temp: {temp:.1f}°C, Humidity: {humidity:.1f}%")
150```
151
152### APDS9960 (Gesture, Proximity, Color Sensor)
153
154```python
155class APDS9960:
156 def __init__(self, i2c, address=0x39):
157 self.i2c = i2c
158 self.address = address
159 self._init_sensor()
160
161 def _init_sensor(self):
162 # Enable device
163 self.i2c.writeto_mem(self.address, 0x80, bytes([0x01]))
164 # Enable gesture mode
165 self.i2c.writeto_mem(self.address, 0x93, bytes([0x01]))
166
167 def read_gesture(self):
168 # Read gesture FIFO
169 fifo_level = self.i2c.readfrom_mem(self.address, 0xAE, 1)[0]
170 if fifo_level > 0:
171 data = self.i2c.readfrom_mem(self.address, 0xFC, 4)
172 # Process gesture data
173 return self._detect_gesture(data)
174 return None
175
176 def _detect_gesture(self, data):
177 # Simplified gesture detection
178 if data[0] > data[2]:
179 return "UP"
180 elif data[0] < data[2]:
181 return "DOWN"
182 elif data[1] > data[3]:
183 return "LEFT"
184 else:
185 return "RIGHT"
186
187# Usage
188i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
189gesture_sensor = APDS9960(i2c)
190
191gesture = gesture_sensor.read_gesture()
192if gesture:
193 print(f"Gesture detected: {gesture}")
194```
195
196### VL53L0X (Time-of-Flight Distance Sensor)
197
198```python
199class VL53L0X:
200 def __init__(self, i2c, address=0x29):
201 self.i2c = i2c
202 self.address = address
203
204 def read_distance(self):
205 # Start measurement
206 self.i2c.writeto_mem(self.address, 0x00, bytes([0x01]))
207
208 # Wait for measurement
209 time.sleep(0.05)
210
211 # Read distance (mm)
212 data = self.i2c.readfrom_mem(self.address, 0x14, 2)
213 distance = (data[0] << 8) | data[1]
214 return distance
215
216# Usage
217i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
218tof = VL53L0X(i2c)
219
220distance = tof.read_distance()
221print(f"Distance: {distance}mm")
222```
223
224## SPI Communication
225
226### SPI Setup
227
228```python
229from machine import SPI, Pin
230
231# Initialize SPI
232spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
233 sck=Pin(2), mosi=Pin(3), miso=Pin(4))
234
235# Chip select pin
236cs = Pin(5, Pin.OUT)
237cs.value(1) # Deselect initially
238
239# Read/write data
240cs.value(0) # Select device
241spi.write(bytes([0x01, 0x02, 0x03])) # Write data
242data = spi.read(3) # Read 3 bytes
243cs.value(1) # Deselect device
244
245print(f"Received: {data}")
246```
247
248### SD Card Reader (SPI)
249
250```python
251from machine import SPI, Pin
252import sdcard
253import os
254
255# Initialize SPI and SD card
256spi = SPI(0, baudrate=1000000, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
257cs = Pin(5, Pin.OUT)
258
259sd = sdcard.SDCard(spi, cs)
260
261# Mount SD card
262os.mount(sd, '/sd')
263
264# Write file
265with open('/sd/data.txt', 'w') as f:
266 f.write('Hello from Badger!')
267
268# Read file
269with open('/sd/data.txt', 'r') as f:
270 print(f.read())
271
272# Unmount
273os.umount('/sd')
274```
275
276## Analog Input (ADC)
277
278```python
279from machine import ADC, Pin
280
281# Initialize ADC on GPIO pin
282adc = ADC(Pin(26))
283
284# Read raw value (0-65535 for 16-bit ADC)
285raw_value = adc.read_u16()
286print(f"Raw ADC: {raw_value}")
287
288# Convert to voltage (assuming 3.3V reference)
289voltage = (raw_value / 65535) * 3.3
290print(f"Voltage: {voltage:.2f}V")
291
292# Example: Read potentiometer
293while True:
294 value = adc.read_u16()
295 percentage = (value / 65535) * 100
296 print(f"Pot: {percentage:.1f}%")
297 time.sleep(0.1)
298```
299
300## NeoPixel (WS2812B) LEDs
301
302```python
303from machine import Pin
304import neopixel
305import time
306
307# Initialize NeoPixel strip (8 LEDs on pin 25)
308num_leds = 8
309np = neopixel.NeoPixel(Pin(25), num_leds)
310
311# Set individual LED color (R, G, B)
312np[0] = (255, 0, 0) # Red
313np[1] = (0, 255, 0) # Green
314np[2] = (0, 0, 255) # Blue
315np[3] = (255, 255, 0) # Yellow
316np.write() # Update LEDs
317
318# Rainbow effect
319def rainbow_cycle(wait):
320 for j in range(255):
321 for i in range(num_leds):
322 pixel_index = (i * 256 // num_leds) + j
323 np[i] = wheel(pixel_index & 255)
324 np.write()
325 time.sleep(wait)
326
327def wheel(pos):
328 """Generate rainbow colors across 0-255 positions"""
329 if pos < 85:
330 return (pos * 3, 255 - pos * 3, 0)
331 elif pos < 170:
332 pos -= 85
333 return (255 - pos * 3, 0, pos * 3)
334 else:
335 pos -= 170
336 return (0, pos * 3, 255 - pos * 3)
337
338rainbow_cycle(0.001)
339```
340
341## Servo Motor Control
342
343```python
344from machine import Pin, PWM
345import time
346
347class Servo:
348 def __init__(self, pin):
349 self.pwm = PWM(Pin(pin))
350 self.pwm.freq(50) # 50Hz for servo
351
352 def angle(self, degrees):
353 """Set servo angle (0-180 degrees)"""
354 # Convert angle to duty cycle
355 # 0° = 1ms (3.2% duty)
356 # 90° = 1.5ms (7.5% duty)
357 # 180° = 2ms (10% duty)
358 min_duty = 1638 # 2.5% of 65535
359 max_duty = 8192 # 12.5% of 65535
360 duty = int(min_duty + (degrees / 180) * (max_duty - min_duty))
361 self.pwm.duty_u16(duty)
362
363 def deinit(self):
364 self.pwm.deinit()
365
366# Usage
367servo = Servo(25)
368
369# Sweep servo
370for angle in range(0, 181, 10):
371 servo.angle(angle)
372 time.sleep(0.1)
373
374servo.deinit()
375```
376
377## Relay Control
378
379```python
380from machine import Pin
381import time
382
383class Relay:
384 def __init__(self, pin):
385 self.pin = Pin(pin, Pin.OUT)
386 self.off()
387
388 def on(self):
389 self.pin.value(1)
390
391 def off(self):
392 self.pin.value(0)
393
394 def toggle(self):
395 self.pin.toggle()
396
397# Usage
398relay = Relay(25)
399
400relay.on()
401time.sleep(2)
402relay.off()
403
404# Pulse relay
405for i in range(5):
406 relay.toggle()
407 time.sleep(0.5)
408```
409
410## Integration with Badge Display
411
412### Display Sensor Data
413
414```python
415import badger2040
416from machine import I2C, Pin
417import time
418
419badge = badger2040.Badger2040()
420i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
421
422def display_sensor_data():
423 # Read sensor (example)
424 temp = read_temperature() # Your sensor function
425 humidity = read_humidity()
426
427 badge.set_pen(15)
428 badge.clear()
429 badge.set_pen(0)
430
431 badge.text("Sensor Monitor", 10, 10, scale=2)
432 badge.text(f"Temp: {temp:.1f}C", 10, 40, scale=2)
433 badge.text(f"Humidity: {humidity:.1f}%", 10, 70, scale=2)
434
435 badge.update()
436
437while True:
438 display_sensor_data()
439 time.sleep(1)
440```
441
442### Interactive Hardware Control
443
444```python
445import badger2040
446from machine import Pin
447import time
448
449badge = badger2040.Badger2040()
450led = Pin(25, Pin.OUT)
451led_state = False
452
453def draw_ui():
454 badge.set_pen(15)
455 badge.clear()
456 badge.set_pen(0)
457
458 badge.text("LED Control", 10, 10, scale=2)
459
460 status = "ON" if led_state else "OFF"
461 badge.text(f"Status: {status}", 10, 50, scale=2)
462
463 badge.text("A: Toggle", 10, 100, scale=1)
464
465 badge.update()
466
467while True:
468 draw_ui()
469
470 if badge.pressed(badger2040.BUTTON_A):
471 led_state = not led_state
472 led.value(1 if led_state else 0)
473 time.sleep(0.2) # Debounce
474```
475
476## Power Management for Hardware
477
478```python
479from machine import Pin
480import machine
481
482# Power control for external hardware
483power_pin = Pin(23, Pin.OUT)
484
485def power_on():
486 power_pin.value(1)
487
488def power_off():
489 power_pin.value(0)
490
491# Use with sleep modes
492power_on()
493# ... do work with sensor ...
494power_off()
495machine.lightsleep(5000) # Sleep 5 seconds
496```
497
498## Troubleshooting
499
500**I2C device not detected**: Check wiring, verify device address, ensure pull-up resistors are present
501
502**GPIO not working**: Verify pin is not used by internal badge functions, check if pin is input/output capable
503
504**SPI communication fails**: Check clock polarity and phase, verify baudrate is within device specs
505
506**PWM not smooth**: Increase PWM frequency, ensure duty cycle calculations are correct
507
508**Sensor readings unstable**: Add delays between readings, use averaging, check power supply stability
509
510## Hardware Safety
511
512- **Never exceed 3.3V** on GPIO pins
513- Use level shifters for 5V devices
514- Add current-limiting resistors for LEDs
515- Use flyback diodes with motors and relays
516- Keep I2C wires short (< 20cm) or use bus extenders
517- Use proper power supply for high-current devices
518
519## Pinout Reference
520
521Common Badger 2350 pins available for external hardware:
522- GPIO 0-22: General purpose I/O
523- GPIO 26-28: ADC capable (analog input)
524- I2C QWIIC: SCL (Pin 5), SDA (Pin 4)
525- SPI: SCK, MOSI, MISO (check documentation)
526
527Refer to official Badger 2350 pinout diagram for complete details.
528