import machine
import time
import uasyncio as asyncio
from machine import Pin, ADC, PWM
print("--- SYSTEM STARTUP: ALL-OFF NO-PROXIMITY ENGINE ---")
# --- PIN DEFINITIONS ---
BATTERY_ADC_PIN = 26 # GP26 (ADC0)
WAKEUP_PIN = 2 # GP2 -> Wakeup from sleep & Runtime Vibration Tracker
PROXIMITY_PIN = 3 # GP3 -> Dedicated Proximity Sensor (Active-Low)
LOAD_CAM_PIN = 22 # GP6 - Controls Camera Power (Relay/MOSFET)
LOAD_TX_PIN = 21 # GP21 - Controls Transmitter Power (Relay/MOSFET)
LOAD_AUX_PIN = 20 # GP20 - Controls Auxiliary Load (Relay/MOSFET)
VIDEO_SW_PIN = 19 # GP19 - Controls Analog Video Switch/Relay
INDICATOR_LED = 8 # GP8 - Status Indicator LED
BUZZER_PIN = 28 # GP28 - Audio PWM Buzzer
# --- SYSTEM CONSTANTS ---
SLEEP_TIMEOUT_SEC = 15
VOLTAGE_SCALE = 12.0 / 3.3
ADC_MAX_V = 3.3
ADC_RESOLUTION = 65535
# --- GLOBAL STATES ---
last_motion_time = time.time()
blink_interval_ms = 1000
is_sleeping = False
# --- HARDWARE INITIALIZATION ---
# Strict Safe Guard: Initialize ALL 4 relay pins explicitly to 0 (OFF)
cam_relay = Pin(LOAD_CAM_PIN, Pin.OUT, value=0)
tx_relay = Pin(LOAD_TX_PIN, Pin.OUT, value=0)
aux_relay = Pin(LOAD_AUX_PIN, Pin.OUT, value=0)
video_switch = Pin(VIDEO_SW_PIN, Pin.OUT, value=0)
led = Pin(INDICATOR_LED, Pin.OUT, value=0)
# Initialize Buzzer Pin as an inactive PWM slice
buzzer_pwm = PWM(Pin(BUZZER_PIN))
buzzer_pwm.duty_u16(0) # Silent
# Inputs with internal Pull-Ups for Active-Low operation
prox_sensor = Pin(PROXIMITY_PIN, Pin.IN, Pin.PULL_UP)
motion_sensor = Pin(WAKEUP_PIN, Pin.IN, Pin.PULL_UP)
bat_adc = ADC(Pin(BATTERY_ADC_PIN))
# --- PWM AUDIO UTILITIES ---
def buzzer_on(frequency=1000):
"""Generates an explicit oscillating tone frequency on GP28."""
buzzer_pwm.freq(frequency)
buzzer_pwm.duty_u16(32768) # 50% duty cycle creates optimal square wave volume
def buzzer_off():
"""Drops the duty cycle back down to zero to kill the audio line."""
buzzer_pwm.duty_u16(0)
def kill_all_relays():
"""Forced hard-shutdown across all control pins."""
cam_relay.value(0)
tx_relay.value(0)
aux_relay.value(0)
video_switch.value(0)
print("[RELAYS] All lines forced safely to LOW/OFF state.")
# --- COOPERATIVE ASYNC WORKERS ---
async def monitor_proximity():
"""Independent Task: Tracks Proximity on GP3.
Switches on Camera, TX, and Video Switch when target is close.
Keeps Aux explicitly off, and clears everything when target is away."""
last_state = -1
while True:
if not is_sleeping:
# Active-Low: 0 means Pressed/GND (Target Near GP3)
is_near = (prox_sensor.value() == 0)
if is_near != last_state:
if is_near:
print("[PROXIMITY] TARGET DETECTED! Energizing Camera, TX, and Signal Relays.")
cam_relay.value(1)
tx_relay.value(1)
video_switch.value(1)
aux_relay.value(0) # Kept explicitly OFF
# Target Alert: Clean double beep tone (1200Hz) & Double LED Flash
for _ in range(2):
led.value(1)
buzzer_on(1200)
await asyncio.sleep_ms(80)
led.value(0)
buzzer_off()
await asyncio.sleep_ms(80)
else:
print("[PROXIMITY] TARGET LOST. Returning all lines to safe OFF state.")
kill_all_relays()
last_state = is_near
await asyncio.sleep_ms(50)
async def monitor_battery():
global blink_interval_ms
while True:
if not is_sleeping:
try:
raw = bat_adc.read_u16()
v_bat = (raw / ADC_RESOLUTION) * ADC_MAX_V * VOLTAGE_SCALE
except:
v_bat = 12.0
if v_bat >= 11.8:
blink_interval_ms = 1000
band = "HIGH"
elif 10.8 <= v_bat < 11.8:
blink_interval_ms = 400
band = "MEDIUM"
else:
blink_interval_ms = 150
band = "LOW"
print(f"[BATTERY] {v_bat:.2f}V | Band: {band}")
await asyncio.sleep(3)
async def led_blinker():
while True:
if not is_sleeping and blink_interval_ms > 0:
led.value(not led.value())
await asyncio.sleep_ms(blink_interval_ms)
else:
led.value(0)
await asyncio.sleep_ms(100)
async def monitor_inactivity():
"""Manages tracking timeouts and handles global low-power standby cuts."""
global last_motion_time, is_sleeping
while True:
# Check Vibration/Motion input on GP2
if motion_sensor.value() == 0:
last_motion_time = time.time() # Reset inactivity timer
if is_sleeping:
print("\n=========================================")
print("[WAKEUP] Motion Detected on GP2! Awakening Core Loop.")
print("=========================================")
is_sleeping = False
last_motion_time = time.time()
# Wakeup Indication: Rapid ascending energetic double chirp
led.value(1)
buzzer_on(1500)
await asyncio.sleep_ms(70)
buzzer_on(1800)
await asyncio.sleep_ms(90)
led.value(0)
buzzer_off()
elapsed = time.time() - last_motion_time
if not is_sleeping and elapsed >= SLEEP_TIMEOUT_SEC:
print(f"\n[SLEEP] {SLEEP_TIMEOUT_SEC}s Idle Timeout. Entering Standby Mode.")
is_sleeping = True
kill_all_relays() # Clean kill line states
print("[SLEEP] Emitting audio-visual sleep notification sequence...")
# Sleep Indicator: 3 distinct descending chirp tones (800Hz, 600Hz, 400Hz)
frequencies = [800, 600, 400]
for freq in frequencies:
led.value(1)
buzzer_on(freq)
await asyncio.sleep_ms(120)
led.value(0)
buzzer_off()
await asyncio.sleep_ms(80)
print("[SLEEP] System Standby. Ground GP2 to Wake up.\n")
await asyncio.sleep_ms(100)
# --- ENGINE ENTRY POINT ---
async def main():
print("==============================================")
print(" Master Control Engine Active (All-Off Design) ")
print("==============================================")
# Initialize complete infrastructure lines on cold boot to LOW/OFF
is_sleeping = False
kill_all_relays()
buzzer_off()
# Launch concurrent tasks
asyncio.create_task(monitor_proximity())
asyncio.create_task(monitor_battery())
asyncio.create_task(led_blinker())
asyncio.create_task(monitor_inactivity())
while True:
await asyncio.sleep(1)
# Start execution loop
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Stopped.")Proximity
Vibration
CAM
Transmitter
AUX
Video SW