[수경재배기 자동 컨트롤러 제작] 6-5. 각 버튼 콜백함수를 이용하여 LED 및 물펌프, 산소 작동시키기(feat. 산소 불 필요시 다른 용도로 가능)

생활/수경재배|2022. 5. 21. 22:14

이제 버튼 배치랑 설정 폼 까지 꾸며 봤으니,

배치한 버튼을 콜백함수를 이용하여 작동 시켜 보기로 함.

 

콜백함수란?

간단하게 설명하자면

어떤 이벤트에 의해 호출되어지는 함수

를 콜백함수라고 함.

 

l1=Button(iotwin, text='LED 1', bg='white',command=lc1,font=('나눔바른펜OTF',35))
l1ab=Checkbutton(iotwin,text="자동",font=('나눔바른펜OTF',20),variable=l1av,command=l1avp)

예를 들어,

위의 버튼과 체크 버튼 소스를 보면

command= 라는 문구가 보이는데

버튼(혹은 체크 버튼)을 클릭하면 실행되는 함수를 지정하는 것임.

즉, 저기에 지정한 함수가 콜백함수가 되는 것임.

 

위에서는 버튼을 클릭하면 lc1함수가 호출(콜백함수)이

체크버튼을 클릭하면 l1av함수가 호출(콜백함수)이 된다는 의미.

이건 전에도 대충 설명했으나, 다시 한번 상기시켜봄.

 

암튼, 이번엔

이 각 버튼에 지정된 콜백함수를 이용하여 LED, 물펌프, 산소등을 작동 시켜 볼까 함.

 

전에 이어서...


    
#종료    
jq=False

def quit1():
    if messagebox.askokcancel("Quit","종료하시겠습니까?"):
        global jq
        jq=True
        GPIO.cleanup()
        nw.quit()
        nw.destroy()
        iotwin.quit()
        iotwin.destroy()
        exit()
        
def quit2():
    nw.withdraw()
    iotwin.deiconify()
    
# LED 물펌프 산소 설정
def lc1():
    relay_2=5
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_2,GPIO.OUT)
    if(l1['bg']=='white'):
        l1['bg']='green'
        GPIO.output(relay_2,GPIO.LOW)
        
    else:
        l1['bg']='white'
        relay_2=5
        GPIO.output(relay_2,GPIO.HIGH)
        GPIO.cleanup(relay_2)

def lc2():
    relay_3=6
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_3,GPIO.OUT)
    if(l2['bg']=='white'):
        l2['bg']='green'
        GPIO.output(relay_3,GPIO.LOW)
    else:
        l2['bg']='white'
        GPIO.output(relay_3,GPIO.HIGH)
        GPIO.cleanup(relay_3)

def lc3():
    relay_4=13
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_4,GPIO.OUT)
    if(l3['bg']=='white'):
        l3['bg']='green'
        GPIO.output(relay_4,GPIO.LOW)
    else:
        l3['bg']='white'
        GPIO.output(relay_4,GPIO.HIGH)
        GPIO.cleanup(relay_4)

def lc4():
    relay_5=19
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_5,GPIO.OUT)
    if(l4['bg']=='white'):
        l4['bg']='green'
        GPIO.output(relay_5,GPIO.LOW)
    else:
        l4['bg']='white'
        GPIO.output(relay_5,GPIO.HIGH)
        GPIO.cleanup(relay_5)

def wc1():
    relay_7=21
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_7,GPIO.OUT)
    if(w1['bg']=='white'):
        w1['bg']='green'
        #o1['bg']='green'
        GPIO.output(relay_7,GPIO.LOW)
    else:
        w1['bg']='white'
        #o1['bg']='white'
        GPIO.output(relay_7,GPIO.HIGH)
        GPIO.cleanup(relay_7)

def oc1():
    relay_8=22
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_8,GPIO.OUT)
    if(o1['bg']=='white'):
        o1['bg']='green'
        GPIO.output(relay_8,GPIO.LOW)
    else:
        o1['bg']='white'
        GPIO.output(relay_8,GPIO.HIGH)
        GPIO.cleanup(relay_8)


#def stc():
 #   print("설정")
    


def k1():
    l1.configure(bg="green")
    relay_2=5
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_2,GPIO.OUT)
    GPIO.output(relay_2,GPIO.LOW)


    


def k2():
    l2.configure(bg="green")
    relay_3=6
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_3,GPIO.OUT)
    GPIO.output(relay_3,GPIO.LOW)


def k3():
    l3.configure(bg="green")
    relay_4=13
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_4,GPIO.OUT)
    GPIO.output(relay_4,GPIO.LOW)


def k4():
    l4.configure(bg="green")
    relay_5=19
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_5,GPIO.OUT)
    GPIO.output(relay_5,GPIO.LOW)

def k5():
    w1.configure(bg="green")
    #o1.configure(bg="green")
    relay_7=21
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_7,GPIO.OUT)
    GPIO.output(relay_7,GPIO.LOW)

#def k6():
#    print ("당첨")
#    o1.configure(bg="green")

def k11():
    l1.configure(bg="white")
    relay_2=5
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_2,GPIO.OUT)
    GPIO.output(relay_2,GPIO.HIGH)
    GPIO.cleanup(relay_2)



def k21():
    l2.configure(bg="white")
    relay_3=6
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_3,GPIO.OUT)
    GPIO.output(relay_3,GPIO.HIGH)
    GPIO.cleanup(relay_3)


def k31():
    l3.configure(bg="white")
    relay_4=13
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_4,GPIO.OUT)
    GPIO.output(relay_4,GPIO.HIGH)
    GPIO.cleanup(relay_4)


def k41():
    l4.configure(bg="white")
    relay_5=19
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_5,GPIO.OUT)
    GPIO.output(relay_5,GPIO.HIGH)
    GPIO.cleanup(relay_5)

def k51():
    w1.configure(bg="white")
    #o1.configure(bg="white")
    relay_7=21
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_7,GPIO.OUT)
    GPIO.output(relay_7,GPIO.HIGH)
    GPIO.cleanup(relay_7)

#def k61():
#    print ("당첨F")
#    o1.configure(bg="white")




    

#LED 물펌프 산소 자동 수동
def l1avp():
#    nt='({})'.format(datetime.datetime.now().strftime('%y/%m/%d %H:%M:%S'))
#    nt1= int(datetime.datetime.now().strftime('%y%m%d%H%M'))
    HT1= int(time.strftime('%H%M'))
    L1OH=int(lt1toh.get())        
    L1OM=int(lt1tom.get())
    L1FH=int(lt1tfh.get())
    L1FM=int(lt1tfm.get())
    HT2=int('{0:04d}'.format(HT1))
#    HT3=int(datetime.datetime.now().strftime('%y%m%d'))
    #HT4=int('{0:06d}'.format(HT3))
    L1OT=int('{0:02d}''{1:02d}'.format(L1OH,L1OM))
    #L1OT1=int('{0:06d}''{1:04d}'.format(HT3,L1OT))
    #    schedule.every().day.at(L1OT).do(k1)
    L1FT=int('{0:02d}''{1:02d}'.format(L1FH,L1FM))
    #L1FT1=int('{0:06d}''{1:04d}'.format(HT3,L1FT))
    #    schedule.every().day.at(L1FT).do(k11)
    #    schedule.run_pending()
    if(l1av.get()==1):
        #print("자동")
        l1.configure(command=())

        #if(L1OT==0 and L1FT==0 or L1OT==L1FT):
        #    l1ab.deselect()
        #    l1ab['state']='disable'
        #    l1.configure(command=lc1)
        #    #threading.Timer(1,l1avp).start()
        #    l1.after(1000,l1avp)            
        #else:
        #    l1ab.select()
        #    l1ab['state']='enable'
        #    l1.configure(command=())
            #if(L1OT==L1FT):
                #l1ab.deselect()
                #l1ab['state']='disable'
                #l1.configure(command=lc1)
                #threading.Timer(1,l1avp).start()
            #else:
        if(L1OT<=HT2 and L1FT>HT2):
            k1()
        else:
            if(L1FT<=HT2 and L1OT>HT2):
                k11()
            else:
                if(L1OT>HT2 and L1FT>HT2):
                    if L1OT<L1FT:
                        k11()
                    else:
                        if L1OT>L1FT:
                            k1()
                else:
                    if(L1OT<HT2 and L1FT<=HT2):
                        if L1OT<L1FT:
                            k11()
                        else:
                            if L1OT>L1FT:
                                k1()
        #print(L1OT, HT2 )
        l1.after(1000,l1avp)

        #print(L1OT,HT2,L1OT1,nt1, "=", L1FT1,nt1)
 
    else:
        #print("수동")
        l1.configure(command=lc1)
    #TR=threading.Timer(1,l1avp)
    #schedule.every(1).seconds.do(l1avp)
    #schedule.run_pending()

def l2avp():
    HT1= int(time.strftime('%H%M'))
    L1OH=int(lt1toh.get())        
    L1OM=int(lt1tom.get())
    L1FH=int(lt1tfh.get())
    L1FM=int(lt1tfm.get())
    HT2=int('{0:04d}'.format(HT1))
    L1OT=int('{0:02d}''{1:02d}'.format(L1OH,L1OM))
    #    schedule.every().day.at(L1OT).do(k1)
    L1FT=int('{0:02d}''{1:02d}'.format(L1FH,L1FM))
    #    schedule.every().day.at(L1FT).do(k11)
    #    schedule.run_pending()
    if(l2av.get()==1):
        #print("자동")
        l2.configure(command=())

        if(L1OT<=HT2 and L1FT>HT2):
            k2()
        else:
            if(L1FT<=HT2 and L1OT>HT2):
                k21()
            else:
                if(L1OT>HT2 and L1FT>HT2):
                    if L1OT<L1FT:
                        k21()
                    else:
                        if L1OT>L1FT:
                            k2()
                else:
                    if(L1OT<HT2 and L1FT<=HT2):
                        if L1OT<L1FT:
                            k21()
                        else:
                            if L1OT>L1FT:
                                k2()
        l2.after(1000,l2avp)
    else:
        #print("수동")
        l2.configure(command=lc2)
                

def l3avp():
    HT1= int(time.strftime('%H%M'))
    L1OH=int(lt1toh.get())        
    L1OM=int(lt1tom.get())
    L1FH=int(lt1tfh.get())
    L1FM=int(lt1tfm.get())
    HT2=int('{0:04d}'.format(HT1))
    L1OT=int('{0:02d}''{1:02d}'.format(L1OH,L1OM))
    #    schedule.every().day.at(L1OT).do(k1)
    L1FT=int('{0:02d}''{1:02d}'.format(L1FH,L1FM))
    #    schedule.every().day.at(L1FT).do(k11)
    #    schedule.run_pending()
    if(l3av.get()==1):
        #print("자동")
        l3.configure(command=())
        if(L1OT<=HT2 and L1FT>HT2):
            k3()
        else:
            if(L1FT<=HT2 and L1OT>HT2):
                k31()
            else:
                if(L1OT>HT2 and L1FT>HT2):
                    if L1OT<L1FT:
                        k31()
                    else:
                        if L1OT>L1FT:
                            k3()
                else:
                    if(L1OT<HT2 and L1FT<=HT2):
                        if L1OT<L1FT:
                            k31()
                        else:
                            if L1OT>L1FT:
                                k3()
        l3.after(1000,l3avp)    
    else:
        #print("수동")
        l3.configure(command=lc3)

def l4avp():
    HT1= int(time.strftime('%H%M'))
    L1OH=int(lt1toh.get())        
    L1OM=int(lt1tom.get())
    L1FH=int(lt1tfh.get())
    L1FM=int(lt1tfm.get())
    HT2=int('{0:04d}'.format(HT1))
    L1OT=int('{0:02d}''{1:02d}'.format(L1OH,L1OM))
    #    schedule.every().day.at(L1OT).do(k1)
    L1FT=int('{0:02d}''{1:02d}'.format(L1FH,L1FM))
    #    schedule.every().day.at(L1FT).do(k11)
    #    schedule.run_pending()
    if(l4av.get()==1):
        #print("자동")
        l4.configure(command=())
        if(L1OT<=HT2 and L1FT>HT2):
            k4()
        else:
            if(L1FT<=HT2 and L1OT>HT2):
                k41()
            else:
                if(L1OT>HT2 and L1FT>HT2):
                    if L1OT<L1FT:
                        k41()
                    else:
                        if L1OT>L1FT:
                            k4()
                else:
                    if(L1OT<HT2 and L1FT<=HT2):
                        if L1OT<L1FT:
                            k41()
                        else:
                            if L1OT>L1FT:
                                k4()        
        l4.after(1000,l4avp)
    else:
        #print("수동")
        l4.configure(command=lc4)

WLOT=0
WLFT=0     
o=0
def w1avp():
    owt=int(time.strftime('%H%M'))
    L1OH=int(wt1toh.get())        
    L1OM=int(wt1tom.get())
    L1FH=int(wt1tfh.get())
    L1FM=int(wt1tfm.get())
    HT2=int('{0:04d}'.format(owt))
    #HT3=int('{0:04d}'.format(ntime1))
    L1OT=int('{0:02d}''{1:02d}'.format(L1OH,L1OM))
    L1FT=int('{0:02d}''{1:02d}'.format(L1FH,L1FM))
    global WLOT
    global WLFT
    global o

    #L1OT1=HT3+L1OT
    if(w1av.get()==1):
        #print("자동")
        w1.configure(command=())
        #o1.configure(command=())
        MO=int(L1OH*60)+L1OM
        MF=int(L1FH*60)+L1FM
    #    schedule.every(MO).minutes.do(k51)
        if(L1OT==0 and L1FT==0):
            k5()
            o=1

        else:
            k5()
            o=1
            if(WLOT==MO):
                k51()
                o=0
                if(WLFT==MF):
                    k5()
                    o=1
                    WLFT=0
                    WLOT=0
                else:
                    WLFT+=1
            else:
                if(WLOT<MO):
                    WLOT+=1
                    

        #print(HT2, WLOT,MO, WLFT, MF)
        w1.after(60000,w1avp)

    else:
        #print("수동")
        w1.configure(command=wc1)
        #o1.configure(command=wc1)
        WLOT=0
        WLFT=0

    #schedule.every(MO).minutes.do(k51)
    #y()

#def y():
#    schedule.run_pending()
#    w1.after(1000,w1avp)

#def o1avp():
#    if(o1av.get()==1):
#        print("자동")
#        o1.configure(command=())
#    else:
#        print("수동")
#        o1.configure(command=oc1)

 

여기서 X버튼을 누르면 프로그램을 종료 되게끔

아래 소스를 작성

#종료    
jq=False

def quit1():
    if messagebox.askokcancel("Quit","종료하시겠습니까?"):
        global jq
        jq=True
        GPIO.cleanup()
        nw.quit()
        nw.destroy()
        iotwin.quit()
        iotwin.destroy()
        exit()
        
def quit2():
    nw.withdraw()
    iotwin.deiconify()

quit1()함수는 메인 화면에서 X버튼을 눌렀을시, 종료하시겠습니까? 라는 메시지창을 나타나게 함.

확인 누르면 종료.

 

quit2()함수는 설정화면에서 X버튼 눌렀을시 설정화면 폼을 빠져나가고, 메인 폼 활성화 시킴.

 

def lc1():
    relay_2=5
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_2,GPIO.OUT)
    if(l1['bg']=='white'):
        l1['bg']='green'
        GPIO.output(relay_2,GPIO.LOW)
        
    else:
        l1['bg']='white'
        relay_2=5
        GPIO.output(relay_2,GPIO.HIGH)
        GPIO.cleanup(relay_2)


def k1():
    l1.configure(bg="green")
    relay_2=5
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(relay_2,GPIO.OUT)
    GPIO.output(relay_2,GPIO.LOW)

이제와 보면서 알았는데..

relay_2=5

  릴레이 2채널에 라즈베리파이 GPIO 5번핀을 사용한다는 건데,

위에 보다시피  다른 함수에도 선언 하였는데,

그때 당시엔 별 생각 없었음.

 

저렇게 하는 것 보다

realy_2=5를 전역으로 선언하는 게 코딩 줄 수도 아끼고 편함.

뭐 저렇게 지역으로 해놓는다고 해도 나쁜 건 아니지만, 좀 귀찮아질수도...

필자는 그냥 별 생각없이 멍때리면서 하다보니... 뭐...

 

여기서는 버튼클릭시 버튼 바탕색이 화이트면  꺼짐(GPIO.HIGH)

그린이면 켜짐(GPIO.LOW)으로 작성 되어 있는데,

보통은 HIGH가 3.3V인가 LOW가 0V 이나,

필자가 사용한 릴레이 8채널이 HIGH일때 릴레이가 꺼지고

LOW일 때 릴레이가 켜지는 방식이라 

이렇게 해둠.테스트 해보고 본인한테 맞게 설정하면 됨.

 

그리고 자동 부분 코딩한 부분은하루 24시간 기준으로

24시가 0으로 되어 버리게 되어서잘못하면

예약타임이 꼬여 버리는 걸 막기 위해 코딩했긴 했는데,

이것도 그냥 막무가내 별 생각없이 그냥 순서대로 대충 생각해가면서

때려 넣은 거라, 이것보다 더 간단한 코딩이 있을 수도 있음.

다시 한번 말하지만, 순간적으로 멍때리면서 하는 경우가 많아

갑자기 산으로 가려다가 돌아온 경우가 군데군데 보일지도 모름. ㅎㅎㅎ

 

근데 하루 24시간 내내 이때까지 계속 아무 탈 없이 돌아가주는 걸로 봐선

 

(DHT22 센서값을 잘 못읽는 거 빼곤...)

(구 버전 라이브러리로는 아예 못 읽어 와 버려서리...)

(그래도 신 버전 라이브러리는 아예 못 읽는 건 아니라...

횟수로 따지면 3번에 한 번 꼴이랄까..)

 

영 형편없는 프로그래밍은 아니었던 걸로...

 

여기에 여러분 재량으로

해당하는 온도에 팬을 돌린다거나,

실내 오픈식이면 에어컨을 가동시킨다던가...

 

양액 온도 같은 경우는 

열전소자(예를 들어 펠티어 소자 같은거)를

외부 전원으로 땡겨서컨트롤 해도 되고... 

필자야 뭐...

정해진 예산 범위내로 최대한 집에 있는 걸로 활용하다보니...

 

솔직히,지금 수경재배기 박막식이라...

산소기가  필요없는데도 불구하고,

예전 담액식 때 사용하던 거 굴러다니는 거 보곤 그냥 집어넣은 거라.

원래 계획은 팬을 수동으로 on/off 할려는 계획이었는데

실내다보니 수경재배기가 오픈되어있고

그리고 32~33도 까지는 에어컨 없어도 어느정도 버텨주길래

팬 설치를 안 했는데,나중에 상황보면서 할 수 있음.

수위 센서 같은 경우는 비접촉 센서라

물 가득 혹은 물 부족 (0과1)로 밖에 안됨.

그래서 이것두 라즈베리파이 GPIO핀을 연결하여0혹은 1일때,

물부족 혹은 물 가득 메시지를휴대폰 메시지로 표시 할 수도 있으나,

필자는 라즈베리파이를 거치지 않고 바로 다이렉트로

물이 모자를때, 빨강 LED가 점등이 되게 해놨음.

 

일단 뭐내가 사용하기에는 그닥 불편하지는 않으나

사용하다보면 기능이 추가되거나, 변경될지도 모름.

앞으로 일은 어떻게 될지 나도 모름. ㅋㅋㅋ

 

근데 팬을 달고 싶어도

달만한 곳을 못찿겠음. ㅋ

사방이 뻥 뚤려있는 오픈식이라... 

어짜피 한다면 12V로 할려고 생각중.

5V는 12V->5V 강압으로 사용하고 있긴한데, 

라즈베리파이랑 터치스크린용으로 만 하는 게 나을 것 같아서

smps에서 12V 출력 지원 하니까, 12V로 땡겨쓰는 게 나을 거라 판단.

 

물론 팬을 달 경우에 그렇다는 얘기임.

암튼 

결론은 팬을 달 마땅한 자리가 없어서

못달고 있음.

 

이상!