from machine import Pin, I2C, ADC, PWM
import utime
import neopixel
# OLED (BitDogLab v7: I2C1 SDA=GP2 SCL=GP3)
try:
from ssd1306 import SSD1306_I2C
except ImportError:
SSD1306_I2C = None
# Pinos
PIN_BTN_A = 5
PIN_BUZZ = 21
PIN_NP = 7
PIN_JOY_X = 27
PIN_JOY_Y = 26
PIN_I2C_SDA = 2
PIN_I2C_SCL = 3
# Globals de IRQ
_btn_event = False
_btn_irq_ms = 0
def _btn_irq(pin):
global _btn_event, _btn_irq_ms
_btn_event = True
_btn_irq_ms = utime.ticks_ms()
def clamp(x, lo, hi):
return lo if x < lo else hi if x > hi else x
def adc_to_pct(adc_u16, center=32768, dead=1500):
# centro -> 0; saturado -> +-100
v = int(adc_u16) - int(center)
if abs(v) < dead:
return 0.0
# escala aproximada
v = clamp(v, -32768, 32767)
return (v / 32767.0) * 100.0
# Init
btn = Pin(PIN_BTN_A, Pin.IN, Pin.PULL_UP)
btn.irq(trigger=Pin.IRQ_FALLING, handler=_btn_irq)
buz = PWM(Pin(PIN_BUZZ))
buz.duty_u16(0)
np = neopixel.NeoPixel(Pin(PIN_NP), 25)
np.fill((0, 0, 0))
np.write()
adc_x = ADC(PIN_JOY_X)
adc_y = ADC(PIN_JOY_Y)
i2c = I2C(1, sda=Pin(PIN_I2C_SDA), scl=Pin(PIN_I2C_SCL), freq=400000)
addrs = i2c.scan()
print("I2C scan:", [hex(a) for a in addrs])
oled = None
if SSD1306_I2C and (0x3c in addrs or 0x3C in addrs):
oled = SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("BitDogLab v7", 0, 0)
oled.text("Wokwi bring-up", 0, 12)
oled.text("I2C ok: 0x3C", 0, 24)
oled.show()
# Estado
mode = 0
last_toggle_ms = 0
DEBOUNCE_MS = 200
def beep(ms=80, freq=2000):
buz.freq(freq)
buz.duty_u16(20000)
t0 = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), t0) < ms:
# evita sleep; só deixa o sim respirar
pass
buz.duty_u16(0)
# Loop não-bloqueante por ticks
t_next = utime.ticks_ms()
PERIOD_MS = 50
while True:
now = utime.ticks_ms()
# Botão com debounce no loop (IRQ só seta flag)
global _btn_event
if _btn_event:
_btn_event = False
if utime.ticks_diff(now, last_toggle_ms) > DEBOUNCE_MS:
last_toggle_ms = now
mode ^= 1
print("Mode:", mode)
beep()
if utime.ticks_diff(now, t_next) >= 0:
t_next = utime.ticks_add(t_next, PERIOD_MS)
x = adc_to_pct(adc_x.read_u16())
y = adc_to_pct(adc_y.read_u16())
# Matriz: um pixel andando conforme X
col = int((x + 100) * 4 / 200) # 0..4
col = clamp(col, 0, 4)
idx = 2 * 5 + col # linha do meio
np.fill((0, 0, 0))
np[idx] = (0, 20, 0) if mode == 0 else (20, 0, 0)
np.write()
if oled:
oled.fill(0)
oled.text("Mode: %d" % mode, 0, 0)
oled.text("X:%6.1f" % x, 0, 16)
oled.text("Y:%6.1f" % y, 0, 28)
oled.show()