import machine
import utime
import urandom
from machine import Pin, PWM, I2C, RTC
import sys
# =========================================================================
# --- MOCK SSD1306 LAYER FOR STANDALONE WOKWI RUNTIME ---
# =========================================================================
class SSD1306_I2C:
def __init__(self, width, height, i2c):
self.width = width
self.height = height
self.i2c = i2c
self.buffer = bytearray((width * height) // 8)
def fill(self, color):
for i in range(len(self.buffer)): self.buffer[i] = 0xFF if color else 0x00
def text(self, string, x, y, color=1): pass
def fill_rect(self, x, y, w, h, color): pass
def rect(self, x, y, w, h, color): pass
def line(self, x1, y1, x2, y2, color): pass
def pixel(self, x, y, color): pass
def show(self):
try: self.i2c.writeto(0x3c, b'\x40' + self.buffer)
except: pass
# Shared I2C Bus for SSD1306 OLED Screen
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
display = SSD1306_I2C(128, 64, i2c)
rtc = RTC()
SW_GAME = Pin(14, Pin.IN, Pin.PULL_UP)
# Simulated Battery Indicators for Wokwi Environment
class MockPin:
def value(self): return 0
charge_sense = MockPin()
quick_charge_sense = MockPin()
# DUAL SONAR RADAR CONFIGURATION
TRIG_INT = Pin(8, Pin.OUT)
ECHO_INT = Pin(9, Pin.IN)
TRIG_NAV = Pin(10, Pin.OUT)
ECHO_NAV = Pin(11, Pin.IN)
# AUDIO MODULE & SERVO LEG ACTUATORS
BUZZER = PWM(Pin(12))
leg_left = PWM(Pin(16))
leg_right = PWM(Pin(17))
leg_left.freq(50)
leg_right.freq(50)
# =========================================================================
# --- 2. GLOBAL SYSTEM CONFIGURATION STATES ---
# =========================================================================
active_game_id, last_switch_state = 1, 1
is_roaming_active = True
stranger_cooldown = 0
is_jelly_terrified = False
simulated_vision_target = 1 # 1 = Family Member, 2 = Stranger Face
# =========================================================================
# --- 3. STANCE ACTUATOR CONTROLS ---
# =========================================================================
def set_leg_positions(left_deg, right_deg):
left_duty = int(2000 + (left_deg / 180) * 6000)
right_duty = int(2000 + (right_deg / 180) * 6000)
leg_left.duty_u16(left_duty)
leg_right.duty_u16(right_duty)
# =========================================================================
# --- 4. RADAR PROXIMITY SENSOR READERS ---
# =========================================================================
def get_distance(trig, echo):
trig.low(); utime.sleep_us(2)
trig.high(); utime.sleep_us(10); trig.low()
timeout = utime.ticks_us()
signaloff = utime.ticks_us()
while echo.value() == 0:
signaloff = utime.ticks_us()
if utime.ticks_diff(signaloff, timeout) > 20000: return 150.0
signalon = utime.ticks_us()
while echo.value() == 1:
signalon = utime.ticks_us()
if utime.ticks_diff(signalon, signaloff) > 20000: return 150.0
return ((signalon - signaloff) * 0.0343) / 2
# =========================================================================
# --- 5. CHIMES & SOUND SYNTHESIZERS ---
# =========================================================================
def emit_sound(freq, length_ms):
if freq == 0: BUZZER.duty_u16(0)
else:
BUZZER.freq(freq); BUZZER.duty_u16(32768)
utime.sleep_ms(length_ms); BUZZER.duty_u16(0)
# =========================================================================
# --- 6. MUSIC PLAYLIST ENGINE ---
# =========================================================================
def execute_birthday_melody():
notes = [262, 262, 294, 262, 349, 330, 262, 262, 294, 262, 392, 349]
beats = [300, 300, 600, 600, 600, 1200, 300, 300, 600, 600, 600, 1200]
for i in range(len(notes)):
set_leg_positions(130, 130) if i % 2 == 0 else set_leg_positions(50, 50)
emit_sound(notes[i], beats[i]); utime.sleep_ms(30)
def execute_christmas_melody():
notes = [330, 330, 330, 330, 330, 330, 330, 392, 262, 294, 330]
beats = [300, 300, 600, 300, 300, 600, 300, 300, 300, 300, 1200]
for i in range(len(notes)):
set_leg_positions(110, 70) if i % 2 == 0 else set_leg_positions(70, 110)
emit_sound(notes[i], beats[i]); utime.sleep_ms(30)
# =========================================================================
# --- 7. WOKWI TERMINAL KEYBOARD PARSER ---
# =========================================================================
import uselect
spoll = uselect.poll()
spoll.register(sys.stdin, uselect.POLLIN)
def check_laptop_keyboard():
global simulated_vision_target
if spoll.poll(0):
char = sys.stdin.read(1)
if char == 's':
simulated_vision_target = 2
print("▶ Simulating AI Cam: [STRANGER DETECTED]")
return 0
if char == 'f':
simulated_vision_target = 1
print("▶ Simulating AI Cam: [FAMILY DETECTED]")
return 0
if char.isdigit():
cmd = int(char)
print(f"▶ Simulating Voice Command: ID {cmd}")
return cmd
return 0
# =========================================================================
# --- 8. OLED FACIAL EXPRESSIONS ---
# =========================================================================
def draw_base_eyes():
display.fill(0)
display.text("STATUS: NORMAL", 10, 4, 1)
display.text("[O O]", 36, 30, 1)
display.show()
def draw_angry_eyes():
display.fill(0)
display.text("!!!STRANGER!!!", 10, 4, 1)
display.text("[\\ /]", 36, 30, 1)
display.show()
def draw_worried_eyes():
display.fill(0)
display.text("HELP ME MOM!!", 10, 4, 1)
display.text("[O O]", 36, 30, 1)
display.show()
def play_angry_bark():
for _ in range(2):
for freq in range(130, 360, 40):
BUZZER.freq(freq); BUZZER.duty_u16(32768); utime.sleep_ms(10)
BUZZER.duty_u16(0); utime.sleep_ms(90)
def play_scared_whimper():
for freq in range(950, 1400, 60):
BUZZER.freq(freq); BUZZER.duty_u16(16384); utime.sleep_ms(5)
BUZZER.duty_u16(0)
# =========================================================================
# --- 9. MASTER SIMULATION LOOP ---
# =========================================================================
print("==================================================")
print(" JELLY PICO V7 SIMULATION INITIALIZED")
print("==================================================")
print("Type these single keys into Wokwi's Serial Box:")
print(" 's' -> Simulate Seeing a Stranger Face")
print(" 'f' -> Simulate Seeing your Family Face")
print(" '1' to '9' -> Simulate Voice Commands")
print("==================================================")
set_leg_positions(90, 90)
while True:
# 1. Parse Keyboard Commands
voice_cmd = check_laptop_keyboard()
if voice_cmd > 0:
is_roaming_active = False
if voice_cmd == 6:
print("Executing: 360 Spin Sequences!"); emit_sound(800, 300)
elif voice_cmd == 4:
execute_birthday_melody()
elif voice_cmd == 5:
execute_christmas_melody()
elif voice_cmd == 8:
is_roaming_active = True; print("Resuming Free Roam Mode!")
# 2. Free Roam Obstacle Avoidance Scanner
if is_roaming_active and simulated_vision_target == 1:
floor_scan = get_distance(TRIG_NAV, ECHO_NAV)
if floor_scan < 30.0:
print(f"Navigation Radar Sensed Obstacle at {floor_scan:.1f}cm!")
emit_sound(400, 100)
utime.sleep_ms(100)
# 3. Dynamic Stranger Logic Engine
if simulated_vision_target == 2: # Activated by typing 's'
is_roaming_active = False
stranger_cooldown = 30
threat_distance = get_distance(TRIG_INT, ECHO_INT)
if threat_distance < 20.0: # Stranger gets too close
is_jelly_terrified = True
draw_worried_eyes()
play_scared_whimper()
set_leg_positions(40, 40) # Cowers down low
else: # Safe distance bark
if not is_jelly_terrified:
set_leg_positions(140, 140) # Stands up tall
draw_angry_eyes()
play_angry_bark()
# Recovery tracking reduction routine
if stranger_cooldown > 0 and simulated_vision_target == 1:
stranger_cooldown -= 1
if stranger_cooldown == 0:
is_jelly_terrified = False
set_leg_positions(90, 90)
is_roaming_active = True
print("Stranger Left! Jelly is safe and roaming happily again.")
# 4. Standard Face Reprint Routine
if simulated_vision_target == 1 and stranger_cooldown == 0:
draw_base_eyes()
utime.sleep_ms(100)