Jian Quan
Created March 18, 2021

Focus Assistant

A device which implements a similar concept like Microsoft Windows Focus Assist. It's main goal is to hide distractions from the user.

Intermediate20 hours34
Focus Assistant

Things used in this project

Hardware components

Stepper motor driver board A4988
SparkFun Stepper motor driver board A4988
×1
Battery, Linear Motors & Drives
Battery, Linear Motors & Drives
×1
Battery Holder, 18650 x 1
Battery Holder, 18650 x 1
×1
Grove - I2C Touch Sensor(MPR121)
Seeed Studio Grove - I2C Touch Sensor(MPR121)
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
×1
Limit Switch, 5 A
Limit Switch, 5 A
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
Rocker Switch, Non Illuminated
Rocker Switch, Non Illuminated
×1
SparkFun Power Cell - LiPo Charger/Booster
SparkFun Power Cell - LiPo Charger/Booster
×1
Raspberry Pi Pi Pico
×1

Software apps and online services

MicroPython
MicroPython

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

Unlocked (CAD)

Locked (CAD)

Schematics

Circuit Diagram

Schematics

CAD image in Locked Mode

CAD image in Unlocked Mode

Code

MicroPython Code for Raspberry Pi Pico

MicroPython
from machine import *
import utime
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd

I2C_ADDR     = 0x3F
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16

buttonl = Pin(6, Pin.IN, Pin.PULL_DOWN)
buttonr = Pin(7, Pin.IN, Pin.PULL_DOWN)
end_pin = Pin(8, Pin.IN, Pin.PULL_DOWN)
potentio = ADC(Pin(26))
dir_pin = Pin(9, Pin.OUT)
step_pin = Pin(10, Pin.OUT)
enable_pin = Pin(11, Pin.OUT)

buttons = [0, 0]
max_time = 60*6
firstrun = True
locked = False
end_time = [0, 0]




i2c = I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)


def lcdWrite(location, data, clear = False):
    if clear:
        lcd.clear()
    if location == 1:
        lcd.move_to(0,0)
    elif location == 2:
        lcd.move_to(0,1)
    elif location == 3:
        lcd.move_to(15,0)
    elif location == 4:
        lcd.move_to(15,1)
        
    lcd.putstr(data)


def buttonStatus():
    global buttons
    
    if buttonl.value():
        waitPinChange(buttonl)
        buttons[0] = 1
    else:
        buttons[0] = 0
        
    if buttonr.value():
        waitPinChange(buttonr)
        buttons[1] = 1
    else:
        buttons[1] = 0

def stepper(lock):
    enable_pin.low()
    if lock:
        dir_pin.high()
    else:
        dir_pin.low()
    
    for i in range (3300):
        step_pin.high()
        utime.sleep_ms(1)
        step_pin.low()
        utime.sleep_ms(1)
        
    enable_pin.high()

def calibrate():
    lcdWrite(1, "Calibrating...", True)
    dir_pin.high()
    enable_pin.low()
#   move towards closed position
    while not end_pin.value():
        step_pin.high()
        utime.sleep_ms(2)
        step_pin.low()
        utime.sleep_ms(2)
    
    enable_pin.high()
    
    
def mintoHour(input_time):
    if input_time > 60:
        minute = input_time % 60
        hours = int(input_time/60)
        minutes = input_time % 60
        return [hours, minutes]
    else:
        return [0, input_time]
    
def getCurrentTime():
    time = utime.localtime()
    current_time = [time[3], time[4]]
    return current_time

def updateEndTime(input_time):
    global end_time
    time = getCurrentTime()
    duration = mintoHour(input_time)
    end_min = time[1] + duration [1]
    end_hour = time[0] + duration[0]
    
    if end_min >=60:
        end_min -= 60
        end_hour += 1
    if end_hour >24:
        end_hour = 24
        
    end_time = [end_hour, end_min]
    file = open("storage_data.txt", "w")
    file.write(str(end_hour) + ":" + str(end_min))
    file.close()
    

def timeDifference(start, end):
    output = [0, 0]
    output[0] = end[0] - start[0]
    output[1] = end[1] - start[1]
    if output[1] < 0:
        output[0] -= 1
        output[1] += 60
    return output

def prelock(duration, mode):
    lcd.clear()
    if mode == 1:
        lcdWrite(1, "Locking for:", True)
        lcdWrite(2, str(duration) + " Minutes")
        
    # time
    if mode == 2:
        lcdWrite(1, "Locking until:", True)
        lcdWrite(2, str(end_time[0]) + ":" + str(end_time[1]))
    stepper(False)
    lcdWrite(4, ">")
    updateEndTime(duration)
    
def lock():
    global locked
    stepper(True)
    print("Countdown")
    lcd.backlight_off()
    lcd.display_off()
    locked = True
    machine.idle()
    
def timeMode(time):
    prelock(time, 2)
    while True:
        buttonStatus()
        if buttons[0]:
            break
        if buttons[1]:
            lock()

    
def durationMode(duration):
    prelock(duration, 1)
    while True:
        buttonStatus()
        if buttons[0]:
            break
        if buttons[1]:
            lock()
            
def timeLCD(time):
    hours = str(time[0])
    minutes = str(time[1])
    if len(hours) == 1:
        hours = "0" + hours
    if len(minutes) == 1:
        minutes = "0" + minutes
    return(hours + ":" + minutes)

def timeMenu():
    global end_time
    lcdWrite(1, "Set Time", True)
    lcdWrite(3, ">")
    while True:
        buttonStatus()
        duration = int(potentio.read_u16()/65535 * max_time)
        updateEndTime(duration)
        if end_time[0] > 12:
            end_time[0] -= 12
        time = timeLCD(end_time)
        lcdWrite(2, "Unlock at " + time)
        utime.sleep(0.1)
        if buttons[0]:
            break
        if buttons[1]:
            timeMode(duration)
            break
        
def durationMenu():
    lcdWrite(1, "Set Duration", True)
    lcdWrite(3, ">")
    while True:
        buttonStatus()
        duration = int(potentio.read_u16()/65535 * max_time)
        hr = mintoHour(duration)
        lcdWrite(2, durationLCD(hr))
        utime.sleep(0.1)
        if buttons[0]:
            break
        if buttons[1]:
            durationMode(duration)
            break

def main():
    enable_pin.high()
#   driver sleeps
    calibrate()
    file = open("storage_data.txt")
    data = file.read()
    if len(data):
        global end_time
        global locked
        end_time = data.split(":")
        for i in range(2):
            end_time[i] = int(end_time[i])
        print(end_time)
        locked = True
        lcd.backlight_off()
        lcd.display_off()
    global firstrun
    while True:
        if firstrun:
            print("Main Menu")
            lcdWrite(1, "      Mode", True)
            lcdWrite(2, "<Time  Duration>")
            firstrun = False
        buttonStatus()
        
        if buttons[0]:
            timeMenu()
            firstrun = True
            
        elif buttons[1]:
            durationMenu()
            firstrun = True
        utime.sleep(0.1)
        
def waitPinChange(pin):
    # wait for pin to change value
    # it needs to be stable for a continuous 20ms
    cur_value = pin.value()
    active = 0
    while active < 20:
        if pin.value() != cur_value:
            active += 1
        else:
            active = 0
        utime.sleep_ms(1)
        
def unlockable():
    global locked
    locked = False
    open("storage_data.txt", "w").close()
    lcd.display_on()
    lcd.backlight_on()
    lcdWrite(1, "Unlocking...", True)
    stepper(False)
    lcdWrite(1, "Unlocked!", True)
    utime.sleep(20)
    reset()

def durationLCD(time):
    hrs = str(time[0])
    mins = str(time[1])
    if len(hrs) == 1:
        hrs = " " + hrs
    if len(mins) == 1:
        mins = " " + mins
    
    return(" " + str(hrs) + "Hrs  " + str(mins) + "Mins")

    
def displayTiming():
    current_time = getCurrentTime()
    time = timeDifference(current_time, end_time)
    if time[0] < 0:
        unlockable()
    
    lcd.display_on()
    lcd.backlight_on()
    lcdWrite(1, "Time Left:", True)
    lcdWrite(2, durationLCD(time))
    utime.sleep(4)
    lcd.backlight_off()
    lcd.display_off()
    
        
def callback(self):
    if locked:
        waitPinChange(buttonr)
        displayTiming()

buttonr.irq(trigger=Pin.IRQ_RISING, handler=callback)

main()

Credits

Jian Quan
2 projects • 0 followers

Comments